# @flowblade/sqlduck 中文文档

规范地址：https://npmcn.com/package/%40flowblade/sqlduck/

版本：0\.47\.0
npm 元数据更新时间：2026-11-11T14:38:34.931Z
中文译文更新时间：2026-11-15T01:10:45.621Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40flowblade/sqlduck
原文对照：https://npmcn.com/package/%40flowblade/sqlduck/?lang=original
项目主页：https://github.com/belgattitude/flowblade
源码仓库：https://github.com/belgattitude/flowblade

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：352 kB，29 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22\.18\.0
- 运行依赖：13 个
- peer 依赖：kysely ^0\.29\.0，valibot ^1\.4\.2，@duckdb/node-api ^2\.0\.0-r\.1
- 首次发布：2025-12-22T20:45:44.036Z
- 最新版本发布：2026-11-11T14:38:34.394Z
- 近 30 天下载量：2292（2026-10-15 至 2026-11-13，npm 官方下载统计）
- 已发布版本数：89（近 12 个月 89 个）

## 中文简介

暂无中文简介

## 安装

```sh
npm install @flowblade/sqlduck
```

也可以使用：`pnpm add @flowblade/sqlduck`、`yarn add @flowblade/sqlduck`、`bun add @flowblade/sqlduck`

## README

## @ flowblade/ sqlduck

> 目前试验

-   来自Zod schemas的 DuckDB 表创建.
-   容易地摄取来自发电机或动能的数据.

## 快开始

### 创建数据库连接

```typescript
import { DuckDBInstance } from "@duckdb/node-api";
DuckDBInstance.create(undefined, {
  access_mode: "READ_WRITE",
  max_memory: "512M",
});
export const conn = await instance.connect();
```

### 将数据添加到数据库

```typescript
import { DuckDatabaseManager, Table } from "@flowblade/sqlduck";
import { DuckTableLoader } from "@flowblade/sqlduck/loader";
import * as z from "zod";

import { conn } from "./db.config.ts";

const dbManager = new DuckDatabaseManager(conn);
const database = await dbManager.attach({
  type: "memory", // can be 'filesystem', ...
  alias: "mydb",
  options: { COMPRESS: "false" },
});

// Define a zod schema, it will be used to create the table
const userSchema = z.object({
  id: z.int32().min(1).meta({ primaryKey: true }),
  name: z.string(),
});

// Example of a datasource (can be an array, generator, async iterable...)
async function* getUsers(): AsyncIterableIterator<z.input<typeof userSchema>> {
  // database or api call
  yield { id: 1, name: "John" };
  yield { id: 2, name: "Jane" };
}

const loader = new DuckTableLoader({ conn });

// Create a table from the schema and load the rows
const result = await loader.load({
  table: new Table({ name: "user", database: database.alias }),
  schema: userSchema, // The schema to use to create the table
  rows: getUsers(), // The rows to load
  // 👇Optional:
  mode: "createOrReplace", // Default is "create"
  progress: {
    intervalMs: 1000, // Default, or everyRows: 100_000
    onProgress: ({ intervalMs, totalRows, rowsPerSecond }) => {
      console.log(
        `Loaded ${totalRows} rows, est: ${rowsPerSecond} rows/s over ${intervalMs}ms`
      );
    },
  },
});

console.log(`Inserted ${result.totalRows} rows in ${result.timeMs}ms`);
console.log(`Table created with DDL: ${result.createTableDDL}`);

const reader = await conn.runAndReadAll("select * from mydb.user");
const rows = reader.getRowObjectsJS();
// [{id: 1, name: 'John'}, {id: 2, name: 'Jane'}]]
```

装入策略(默认情况下的DuckDB附件、行附件、准备的有上行的插入)、模式、检查点、进度和调取在 [装入器文档](https://npmcn.com/package/%40flowblade/sqlduck/docs/loader.md)。 。 。 .

### 导出查询

`QueryExporter` 将参数化的 SQL-tag 查询导出到 DuckDB 的 CSV、 Parquet 或 Excel (XLSX) `COPY`,所以行从不通过JavaScript. 每个持久性战略都有一个方法:

| 方法 | 持久性 | 回返 |
| --- | --- | --- |
| `toFile()` | 您选择和拥有的本地文件 | 文件描述( E)`path`, (中文(简体) ), `sizeBytes`, (中文(简体) ), `rowCount`, (中文(简体) ), `timeMs`. ) (中文(简体) ) |
| `toObjectStorage()` | Azure Blob 存储器、 S3、 GCS 或 R2 中的天体 | 对象描述( A)`url`, (中文(简体) ), `rowCount`, (中文(简体) ), `timeMs`. ) (中文(简体) ) |
| `toResponse()` | 临时文件, 响应发送或取消后删除 | 网络 `Response` 流出下载 |

```typescript
import { DuckdbDatasource, sql } from "@flowblade/source-duckdb";
import { QueryExporter } from "@flowblade/sqlduck/export";

const exporter = new QueryExporter({
  datasource: new DuckdbDatasource({ connection: conn }),
});
```

#### 导出为文件

```typescript
const file = await exporter.toFile({
  sql: sql`SELECT id, name FROM user WHERE active = ${true}`,
  format: { type: "parquet", compression: "zstd" },
  file: "/data/exports/users.parquet",
});

console.log(`${file.path}: ${file.rowCount} rows, ${file.sizeBytes} bytes`);
```

目录 `file` 必须存在。 一个已存在的文件被替换, 但只有在导出完成后: 一个失败的导出没有留下部分文件, 并且保留了先前的文件 .

#### 导出为对象存储

`toObjectStorage` 将导出直接上传到 Azure Blob 存储器(`az://`, (中文(简体) ), `abfss://`页:1`s3://`),谷歌云存储器(`gs://`)或 Cloudflare R2(`r2://`) (中文(简体) ). DuckDB 写给存储器本身: 本地没有写出任何内容 。 其他urls和本地路径被拒绝.

```typescript
import { DuckSecretsManager } from "@flowblade/sqlduck";

await new DuckSecretsManager(conn).create({
  name: "exports_azure",
  type: "azure",
  connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
  scope: "az://exports/",
});

const exported = await exporter.toObjectStorage({
  sql: sql`SELECT id, name FROM user WHERE active = ${true}`,
  format: { type: "parquet" },
  url: "az://exports/2026/users.parquet",
});

console.log(
  `${exported.url}: ${exported.rowCount} rows in ${exported.timeMs}ms`
);
```

出口商不取证书:DuckDB使用其范围与url相匹配的秘密(见 [管理机密](https://npmcn.com/package/%40flowblade/sqlduck/#readme-manage-secrets)) (中文(简体) ). 容器或桶必须存在,并替换一个已存在的物体。 所需的 DuckDB 扩展名(`azure` 或者说 `httpfs`)被安装并加载在第一个导出上,这可能需要网络访问. 在 Azure 上, 失败的导出不会留下局部的blob .

#### 导出为响应

`toResponse` 返回网络标准 `Response` 与 `Content-Type`, (中文(简体) ), `Content-Length` 财务报告和审定财务报表 `Content-Disposition` 信头设定 。 从Hono、Next.js 路线处理器、 Bun 或 Deno 也可以返回:

```typescript
return await exporter.toResponse({
  sql: sql`SELECT id, name FROM user WHERE active = ${true}`,
  format: { type: "csv", header: true },
  filename: "users.csv", // optional, defaults to export.<extension>
  signal: request.signal,
});
```

与 `node:http` 或 Express, 把它插入服务器响应中 :

```typescript
import { Readable } from "node:stream";
import { pipeline } from "node:stream/promises";

const response = await exporter.toResponse({ sql, format });
res.writeHead(response.status, Object.fromEntries(response.headers));
await pipeline(Readable.fromWeb(response.body), res);
```

首先将查询导出为临时文件, 因此在发送任何字节前失败的查询拒绝 。 当主体读取完毕后, 临时文件被删除, 取消( 客户端断开) , 中止通过 `signal` 或者失败。 作为一个安全网,一个其身体无人阅读的反应 `idleTimeoutMs`,在其第一次读取或两次读取之前,失败,其临时文件也被删除.

| 选项 | 默认 | 说明 |
| --- | --- | --- |
| `filename` | `export.<extension>` | 下载文件名 `Content-Disposition` 标题 |
| `directory` | `os.tmpdir()` | 创建临时文件的现有目录 |
| `headers` |  | 附加响应头, 即: `Cache-Control` |
| `idleTimeoutMs` | `60_000` | 最长可等待阅读的时间; `Infinity` 关闭它 |
| `signal` |  | 取消查询,然后将答复流出,是否读取 |

压缩 CSV 作为压缩文件发送 (`application/gzip` 或者说 `application/zstd`, (中文(简体) ), `export.csv.gz`),无 `Content-Encoding`。 。 。 .

#### 导出 Kysely 查询

DuckDB上运行的导出,但行可以通过一个 Kysely 查询从另一个数据库(msql, postgres...)中来. 说吧 `source` 改为 `sql`:它的行被流到一个临时 DuckDB 表格中,导出,然后不管结果如何,该表格都会被丢出. 和我们一起工作 `toFile`, (中文(简体) ), `toObjectStorage` 财务报告和审定财务报表 `toResponse`。 。 。 .

```typescript
import { KyselyQueryWithZodSchema } from "@flowblade/sqlduck/kysely";
import * as z from "zod";

const file = await exporter.toFile({
  source: new KyselyQueryWithZodSchema({
    query: db.selectFrom("user").select(["id", "name"]),
    // Creates the temporary table, must match the selected columns
    schema: z.strictObject({ id: z.int32(), name: z.string() }),
  }),
  format: { type: "parquet" },
  file: "/data/exports/users.parquet",
  // Optional: where the temporary table is created
  materialization: { database: "scratch" }, // e.g. ATTACH 'scratch.duckdb' AS scratch
});
```

`mapQuery` 财务报告和审定财务报表 `selectColumns` 适用于已实现行: `mapQuery` 收到一个 `SELECT` 页:1 各行一次通过JavaScript,从源数据库到DuckDB;到CSV,Parquet或Excel的写入会留在DuckDB内部. `materialization.database` 财务报告和审定财务报表 `materialization.schema` 必须是有效的、非保留的 DuckDB 标识符,而且 计划必须存在.

#### 格式

这个 `format` 选项是受歧视的结合:

| 格式 | 支持的选项 |
| --- | --- |
| `csv` | `header`, (中文(简体) ), `delimiter`, (中文(简体) ), `quote`, (中文(简体) ), `escape`, (中文(简体) ), `nullString`, (中文(简体) ), `dateFormat`, (中文(简体) ), `timestampFormat`, (中文(简体) ), `compression` (单位:千美元)`none`, (中文(简体) ), `gzip`, (中文(简体) ), `zstd`(中文(简体) ) |
| `parquet` | `compression` (单位:千美元)`uncompressed`, (中文(简体) ), `snappy`, (中文(简体) ), `gzip`, (中文(简体) ), `zstd`, (中文(简体) ), `brotli`, (中文(简体) ), `lz4`, (中文(简体) ), `lz4_raw`, (中文(简体) ), `rowGroupSize`, (中文(简体) ), `preserveOrder` |
| `excel` | `header`, (中文(简体) ), `sheet`, (中文(简体) ), `sheetRowLimit` |

Excel使用DuckDB的Excel扩展,安装并加载在第一个Excel导出上,这可能需要网络访问. 出口商本身要求 `DuckdbDatasource`;通过 Kysely 输出查询其他数据库 `source`。 。 。 .

#### 映射导出查询

DuckDB内部有两种选择,每种方法都有:

-   `mapQuery` 重写查询,一般通过将查询包入过滤、排序或添加计算出列来进行.
-   `selectColumns` 选择、重新排序和重命名导出列。 密钥是查询的列名( 以 `mapQuery`页:1 列按照按键的顺序排列.

```typescript
const file = await exporter.toFile({
  sql: sql`SELECT id, name, password FROM user`,
  format: { type: "csv", header: true },
  file: "/data/exports/users.csv",
  mapQuery: (query) =>
    sql`SELECT *, upper(name) AS name_uc FROM (${query}) ORDER BY id`,
  selectColumns: { id: "User ID", name_uc: "Name" },
});
```

#### 日志

`QueryExporter` 日志为 [日志](https://github.com/logtape/logtape) 下划线 `["flowblade", "sqlduck"]` 类别,或与 `logger` 给构造者:

| 职等 | 活动 | 属性 |
| --- | --- | --- |
| `info` | 导出完成 | `format`, (中文(简体) ), `queryTimeMs`, (中文(简体) ), `timeMs`附加时 `path`, (中文(简体) ), `sizeBytes`, (中文(简体) ), `rowCount` (档案), `url`, (中文(简体) ), `rowCount` (对象存储)或 `filename`, (中文(简体) ), `path`, (中文(简体) ), `sizeBytes` (答复) |
| `warning` | 通过它导出已中止 `signal` | `format`, (中文(简体) ), `timeMs` |
| `error` | 导出失败 | `format`, (中文(简体) ), `sql` (查询或Kysely源的文本,没有参数值), `timeMs`, (中文(简体) ), `errorMessage` |
| `error` | 无法删除部分文件或临时文件 | `path`, (中文(简体) ), `errorMessage` |
| `debug` | DuckDB 扩展已装入( D)`excel`, (中文(简体) ), `azure`, (中文(简体) ), `httpfs`(中文(简体) ) | `extension`, (中文(简体) ), `timeMs` |
| `debug` | 实现 Kysely 源 | `table`, (中文(简体) ), `rowCount`, (中文(简体) ), `timeMs` |
| `debug` | 完全读取或取消答复,删除临时文件 | `filename`, (中文(简体) ), `outcome`, (中文(简体) ), `path`, (中文(简体) ), `streamTimeMs` |
| `warning` | 响应中止或未读( Q)`outcome`数字 : `aborted`, (中文(简体) ), `timeout`(中文(简体) ) | `filename`, (中文(简体) ), `outcome`, (中文(简体) ), `path`, (中文(简体) ), `streamTimeMs` |
| `error` | 无法读取响应文件( N)`outcome`数字 : `failed`(中文(简体) ) | `filename`, (中文(简体) ), `outcome`, (中文(简体) ), `path`, (中文(简体) ), `streamTimeMs` |

### 管理机密

`DuckSecretsManager` 创建 DuckDB 秘密,允许访问对象存储(Azure,S3,GCS,R2)或HTTP端点. 它跟随其他管理器:它是由与logtape的连接和日志创建的.

```typescript
import { DuckSecretsManager } from "@flowblade/sqlduck";

const secrets = new DuckSecretsManager(conn);

await secrets.create({
  name: "exports_azure",
  type: "azure",
  connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
  scope: "az://exports/",
});

// Without keys: managed identity, cli, env...
await secrets.create({
  name: "lake_azure",
  type: "azure",
  provider: "credential_chain",
  accountName: "mylake",
});
```

| 方法 | 说明 |
| --- | --- |
| `create(secret, { persistent, onConflict })` | 创造出一个秘密. `onConflict` 实值 `error` (违约), `replace` 或者说 `ignore` |
| `drop(name, { ifExists })` | 丢出一个秘密 |
| `list({ name, type })` | 列出由 DuckDB 编辑的敏感值的秘密 |
| `which(path, type)` | 秘密的 DuckDB 会用于路径,或者 `null` |
| `withSecret(secret, fn)` | 运行 `fn` 暂时保密,完成或抛出时放下 |

-   秘密值被绑定为查询参数,因此它们从未出现在已登录的语句或出错中.
-   秘密默认是暂时的:他们一生生活在DuckDB实例的记忆中. `persistent: true` 在 DuckDB 的秘密目录中保存它们.
-   秘密属于 DuckDB 实例: 它的所有关系都能看到它们,包括 a `withSecret` 在函数运行时隐藏。 使用 `scope` 以限制一个秘密所适用的路径.
-   `azure`, (中文(简体) ), `s3`, (中文(简体) ), `r2`, (中文(简体) ), `gcs` 财务报告和审定财务报表 `http` 秘密被输入。 其他选项进入 `options` (骆驼案件密钥),其他类型 `{ type: "custom", customType }`。 。 。 .
-   创建扩展类型的秘密( N)`azure`, (中文(简体) ), `s3`...)使 DuckDB 安装并加载扩展,这可能需要网络第一次访问.

### 查询已实现的 Kysely 查询

> ⚠️ **实验**:下面的API可能会改变一个小的发布.

`withMaterializedKyselyQuery` 将 Kysely 查询的行( 例如从 mssql 或 postgres) 流入一个临时 DuckDB 表格, 将您的查询运行在它上, 然后丢弃表格, 无论查询成功、 失败或丢弃 .

```typescript
import { DuckDBInstance } from "@duckdb/node-api";
import {
  createKyselyMssqlDialect,
  TediousConnUtils,
} from "@flowblade/source-kysely";
import { sql } from "@flowblade/sql-tag";
import {
  KyselyQueryWithZodSchema,
  withMaterializedKyselyQuery,
} from "@flowblade/sqlduck/kysely";
import { Kysely } from "kysely";
import * as z from "zod";

// Source database: any Kysely instance (here mssql)
type DB = {
  user: { id: number; name: string };
};
const db = new Kysely<DB>({
  dialect: createKyselyMssqlDialect({
    tediousConfig: TediousConnUtils.fromJdbcDsn(
      "sqlserver://localhost:1433;database=db;user=sa;password=pwd"
    ),
  }),
});

// Target duckdb connection (a DuckDBConnection or a DuckdbDatasource)
const instance = await DuckDBInstance.create();
const conn = await instance.connect();

const table = new KyselyQueryWithZodSchema({
  query: db.selectFrom("user").select(["id", "name"]),
  // used to create the temporary table, must match the selected columns
  schema: z.strictObject({ id: z.int32(), name: z.string() }),
});

const result = await withMaterializedKyselyQuery({
  duckConn: conn,
  table,
  // 👇Optional: where the temporary table is created.
  // Defaults to the connection's current database and schema.
  database: "scratch", // e.g. ATTACH 'scratch.duckdb' AS scratch
  schema: "staging", // must already exist
  query: ({ dsDuck, table }) =>
    dsDuck.query(
      sql<{ id: number; name: string }>`
                SELECT id, name FROM ${sql.raw(table.getFullName())} WHERE id < 1000
            `
    ),
});

if (result.error) {
  // materialization or query error
} else {
  console.log(result.data);
  // the first span holds the materialization info (ddl, affectedRows, timeMs, tableName)
  console.log(result.meta.getSpansByType("materialization"));
}
```

`database` 财务报告和审定财务报表 `schema` 必须是有效的、非保留的 DuckDB 标识符,否则返回出错结果,查询不运行。 以所附文件数据库为目标,可使大型材料化不包含在内.

### 交易

斯克尔达克参加 `DuckdbDatasource` 同一连接的交易(参见 [@ flowblade/ source-duckdb (英语)](https://github.com/belgattitude/flowblade/tree/main/packages/source-duckdb#transactions):

-   交易等待运行的负载( e)`toTable()`或管理者查询, 他们等待一个运行的交易,
-   在一次交易召回中,他们在交易中运行:失败的负载会阻止承诺.

```typescript
const ds = new DuckdbDatasource({ connection: conn });
await ds.transactionOrThrow(async (tx) => {
  await tx.queryOrThrow(sql`DELETE FROM product`);
  // rolled back with the delete if anything fails
  await sqlDuck.toTable({ table, schema, rowStream, autoCheckpoint: false });
});
```

> **说明**: DuckDB在交易中无法检查,在交易回调中运行的负载的检查点被跳过( DuckDB) :`autoCheckpoint`, (中文(简体) ), `checkpointChunksFrequency`,则 `checkpoint` 选项 `DuckTableLoader`) (中文(简体) ). 使用 `isInConnectionTransaction(conn)` 从 `@flowblade/source-duckdb` 在你的代码里做同样的事情.

## 兼容性

| 职等 | 中情局 | 说明 |
| --- | --- | --- |
| 节点 | ✅ | 要求22.18+,CI为22x,24x和26x |
| 阿宝 | ✅ | Bun 1.4+,用最新的Bun对CI进行单元测试 |
| 德诺 | ✅ | Deno LTS,与Deno 2一起对CI进行单元测试 |
| DuckDB 函数 | ✅ | `@duckdb/node-api` 2.x (单位:千美元)`^2.0.0-r.1`),1.x 不再支持 |
| 字体 | ✅ | TS 5.0+ + / (单位:千美元) [是- 类型- 错误](https://github.com/arethetypeswrong/arethetypeswrong.github.io) 检查CI. |
| ES2022 (英语) | ✅ | 检查文件 [检查](https://github.com/yowainwright/es-check) |

## 基准

### 节点 24.21

```
 RUN  v5.0.1 /home/sebastien/github/flowblade/packages/sqlduck

 ✓  bench  bench/appender.bench.ts (1 test) 7533ms
   ✓ appender benches 7530ms
     name                                                             hz     min     max    mean     p75     p99    p995    p999      rme  samples
     duckdb appender, count: 100000, chunk size 1024              5.1751  180.11  231.60  194.56  192.86  229.70  230.65  231.41  ±10.12%        6   fastest
     duckdb appender memory, count: 100000, chunk size 2048       5.0295  172.58  244.98  202.55  226.73  244.25  244.62  244.91  ±19.34%        5
     duckdb appender file no wal, count: 100000, chunk size 1024  4.2547  210.67  317.28  240.39  237.64  314.09  315.69  316.96  ±22.80%        5
     duckdb appender file no wal, count: 100000, chunk size 2048  3.8371  200.49  323.42  268.76  295.48  322.31  322.87  323.31  ±30.51%        4
     duckdb appender file, count: 100000, chunk size 2048         3.7207  257.80  287.20  269.21  272.99  286.63  286.91  287.14   ±7.53%        4   slowest
 ✓  bench  bench/stream.bench.ts (1 test) 3986ms
   ✓ Bench rowsToColumnsChunks 3985ms
     name                                                                         hz      min      max     mean      p75      p99     p995     p999      rme  samples
     rowToColumnsChunk with chunkSize 2048 (count: 100000)                   16.4982  53.5190  78.3487  61.5746  70.6733  78.0419  78.1953  78.3180   ±7.00%       17   fastest
     rowToColumnsChunk with transformer with chunkSize 2048 (count: 100000)  13.4073  63.2078   105.13  76.1133  81.6002   102.92   104.03   104.91   ±8.98%       14
     mapFakeRowStream with chunkSize 2048 (count: 100000)                    10.3533  82.5579   120.58  98.5407   113.23   120.25   120.42   120.54  ±10.12%       11   slowest
 ✓  bench  bench/table-create.bench.ts (1 test) 1286ms
   ✓ Bench getTableCreateFromZod 1284ms
     name                          hz     min      max    mean     p75     p99    p995    p999     rme  samples
     getTableCreateFromZod  20,993.53  0.0343  10.9287  0.0597  0.0619  0.2081  0.3518  0.6929  ±4.00%    16755
```

### 1.4.2号舱

```
 RUN  v5.0.1 /home/sebastien/github/flowblade/packages/sqlduck

 ✓  bench  bench/appender.bench.ts (1 test) 7353ms
   ✓ appender benches 7351ms
     name                                                             hz     min     max    mean     p75     p99    p995    p999      rme  samples
     duckdb appender memory, count: 100000, chunk size 2048       6.5010  141.93  167.82  154.19  158.35  167.29  167.55  167.77   ±4.92%        7   fastest
     duckdb appender, count: 100000, chunk size 1024              5.8972  158.33  185.98  170.04  174.01  185.43  185.70  185.92   ±6.09%        6
     duckdb appender file no wal, count: 100000, chunk size 2048  5.1876  181.29  250.19  195.34  189.16  247.18  248.69  249.89  ±14.55%        6
     duckdb appender file no wal, count: 100000, chunk size 1024  4.8410  187.20  264.48  209.80  208.05  262.22  263.35  264.26  ±18.63%        5
     duckdb appender file, count: 100000, chunk size 2048         4.1319  229.30  261.16  242.64  253.36  260.85  261.00  261.13   ±7.09%        5   slowest
 ✓  bench  bench/stream.bench.ts (1 test) 3933ms
   ✓ Bench rowsToColumnsChunks 3931ms
     name                                                                         hz      min      max     mean      p75      p99     p995     p999     rme  samples
     rowToColumnsChunk with chunkSize 2048 (count: 100000)                   24.5296  34.3957  51.2149  41.1676  43.4825  50.3109  50.7629  51.1245  ±4.18%       25   fastest
     rowToColumnsChunk with transformer with chunkSize 2048 (count: 100000)  23.5864  36.3807  57.7801  42.8068  43.2094  56.3888  57.0845  57.6410  ±4.56%       24
     mapFakeRowStream with chunkSize 2048 (count: 100000)                    21.5954  42.7406  51.6524  46.4448  48.1176  51.5462  51.5993  51.6418  ±2.52%       22   slowest
 ✓  bench  bench/table-create.bench.ts (1 test) 1310ms
   ✓ Bench getTableCreateFromZod 1308ms
     name                          hz     min      max    mean     p75     p99    p995    p999     rme  samples
     getTableCreateFromZod  37,765.77  0.0185  10.1286  0.0360  0.0346  0.1235  0.1771  2.3683  ±4.27%    27781
```

### 本地脚本

| 名称 | 说明 |
| --- | --- |
| `pnpm build` |  |
| `pnpm typecheck` |  |
| `pnpm lint` | 检查 lint 错误 |
| `pnpm lint --fix` | 尝试运行自动插件 |
| `pnpm test-unit` | 运行单元测试 |
| `pnpm test-e2e` | 运行单元测试 |

## 常见问题

### 如何安装 @flowblade/sqlduck？

在项目目录运行 npm install @flowblade/sqlduck，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：@duckdb/node-api（^2\.0\.0-r\.1）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @flowblade/sqlduck 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @flowblade/sqlduck 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @flowblade/sqlduck 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.18\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @flowblade/sqlduck 可以商用吗？

@flowblade/sqlduck 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @flowblade/sqlduck 的下载量有多少？

根据 npm 官方下载统计，@flowblade/sqlduck 在 2026/10/15 – 2026/11/13 这 30 天内被下载 2,292 次，日均约 76 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @flowblade/sqlduck 还在维护吗？

最新版本 0\.47\.0 发布于 2026/11/11（4 天前）；至今共发布 89 个版本，其中过去 12 个月发布了 89 个；首次发布于 2025/12/23。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

