DuckDB .Net 客户端 - DuckDB.NET 基本用法

DuckDB

SQL 执行

要在 DuckDB.NET 中执行 SQL 语句,你需要创建一个 DuckDBConnection 对象,它作为执行 SQL 语句的入口点。创建连接后,创建一个与之关联的命令,并使用以下方法之一来执行该命令:

  • ExecuteNonQuery - 执行不返回结果的 SQL,例如 DDL(数据定义语言)语句,或者通过执行 UPDATEINSERTDELETE 语句来更改数据。
  • ExecuteScalar - 执行返回单个标量值的 SQL。
  • ExecuteReader - 执行 SQL 并返回 DuckDBDataReader,可用于处理结果集。
using var duckDBConnection = new DuckDBConnection("Data Source=file.db");
duckDBConnection.Open();

using var command = duckDBConnection.CreateCommand();
command.CommandText = "CREATE TABLE weather (city VARCHAR, temp_lo INTEGER, temp_hi INTEGER, prcp REAL, date DATE);";
var executeNonQuery = command.ExecuteNonQuery();

command.CommandText = "INSERT INTO weather VALUES ('Tbilisi', 41, 55, 0.1, '2020-04-02');";
executeNonQuery = command.ExecuteNonQuery();

command.CommandText = "INSERT INTO weather VALUES ('San Francisco', 46, 50, 0.25, '1994-11-27');";
executeNonQuery = command.ExecuteNonQuery();
using var duckDBConnection = new DuckDBConnection("Data Source=file.db");
duckDBConnection.Open();

using var command = duckDBConnection.CreateCommand();
command.CommandText = "Select count(*) from weather";
var count = command.ExecuteScalar();
using var duckDBConnection = new DuckDBConnection("Data Source=file.db");
duckDBConnection.Open();

using var command = duckDBConnection.CreateCommand();
command.CommandText = "SELECT city, (temp_hi + temp_lo) / 2 AS temp_avg, date FROM weather;";

using var reader = command.ExecuteReader();

while (reader.Read())
{
    Console.WriteLine("City: {0} Average Temperature: {1}, Date: {2}", 
                      reader.GetString(0), reader.GetDouble(1), reader.GetDateTime(2));
}

参数化语句

在构建 SQL 命令时,始终使用参数化查询而不是字符串拼接。这有助于防止 SQL 注入,提高类型安全性,并可能提升性能。

DuckDB 支持在预编译语句中表示参数的三种语法:自动递增(?)、位置($1)和命名($param)。DuckDB.NET 支持所有这三种语法:

using var connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM person WHERE starts_with(name, $name_start_letter) AND age >= $minimum_age;";

command.Parameters.Add(new DuckDBParameter("minimum_age", 40));
command.Parameters.Add(new DuckDBParameter("name_start_letter", "B"));

using var reader = command.ExecuteReader();

批处理

你可以通过连接多个语句并用分号分隔,一次性执行多个语句:

using var connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "INSTALL httpfs;LOAD httpfs;";
command.ExecuteNonQuery();

如果这些语句返回数据,你可以使用 NextResult 方法处理多个结果集:

using var connection = new DuckDBConnection("DataSource=:memory:");
connection.Open();

using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM weather ORDER BY city, temp_lo;SELECT DISTINCT city FROM weather;";
using var reader = command.ExecuteNonQuery();

do
{
  // 处理来自读取器的数据
} while (reader.NextResult());

物化模式和流模式

使用 DuckDBCommand 执行 Select 语句时,DuckDB.NET 默认将使用物化执行模式。在这种模式下,DuckDB 会在内存中获取整个结果集,这可能导致高内存使用。另一种选择是使用流执行模式,在这种情况下,DuckDB 将逐块获取结果集,使用较少的内存,但这可能会导致查询执行速度较慢。要使用流模式,将 UseStreamingMode 属性设置为 true

Publish on 2025-01-05,Update on 2025-02-10