# @civitas-cerebrum/sql-client 中文文档

规范地址：https://npmcn.com/package/%40civitas-cerebrum/sql-client/

版本：0\.1\.3
npm 元数据更新时间：2026-10-23T21:06:26.825Z
中文译文更新时间：2026-11-09T22:42:34.273Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40civitas-cerebrum/sql-client
原文对照：https://npmcn.com/package/%40civitas-cerebrum/sql-client/?lang=original
项目主页：https://github.com/civitas-cerebrum/sql-client#readme
源码仓库：https://github.com/civitas-cerebrum/sql-client

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

- 包体积（解压后）：150 kB，56 个文件
- 模块格式：CommonJS
- TypeScript 类型：包内自带
- Node.js 要求：\>=24
- 运行依赖：1 个
- peer 依赖：pg \*，mssql \*，mysql2 \*，oracledb \*，better-sqlite3 \*
- 首次发布：2026-06-12T20:23:34.607Z
- 最新版本发布：2026-10-23T21:06:26.092Z
- 近 30 天下载量：3103（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：4（近 12 个月 4 个）

## 中文简介

一个轻量级多引擎的SQL客户端(Postgres, MySQL, SQLite, SQL Server, Oracle),拥有流畅的查询构建器,打出结果,以及被默认的查询.

## 安装

```sh
npm install @civitas-cerebrum/sql-client
```

也可以使用：`pnpm add @civitas-cerebrum/sql-client`、`yarn add @civitas-cerebrum/sql-client`、`bun add @civitas-cerebrum/sql-client`

## README

# 多引擎 SQL 客户端

[![NPM Version](https://img.shields.io/npm/v/@civitas-cerebrum/sql-client?color=rgb\(88%2C%20171%2C%2070\))](https://www.npmjs.com/package/@civitas-cerebrum/sql-client)

轻量级 **多引擎** 用于查询和测试自动化的SQL客户端——一个API横跨 **PostgreSQL, MySQL/MariaDB, SQLite, SQL 服务器和甲骨文**,带有流畅的查询构建器,键入结果,并且总是被参数化的查询.

这个包是 **数据层**:用于人类+LLM代理写作的节目SQL外观. 这是兄弟 [`@civitas-cerebrum/wasapi`](https://www.npmjs.com/package/@civitas-cerebrum/wasapi) (HTTP),他们一起支持步骤API [`@civitas-cerebrum/element-interactions`](https://www.npmjs.com/package/@civitas-cerebrum/element-interactions)如果您正在通过此框架编写 E2E 测试规格,请使用其 `steps.sql*` 包件——这个包就是它们的力量, 以及你用固定装置、种子脚本、控制内部和独立的工具来达到的.

* * *

## 一个API,五个引擎

每个数据库驱动程序都有自己的连接仪式,占位符风格,以及结果形状. 多引擎代码通常指5个代码路径.

**在( 驱动程序) 之前 :**

```ts
// pg: Pool, $1 placeholders
const pool = new Pool({ connectionString });
const res = await pool.query('SELECT * FROM books WHERE genre = $1', ['Fiction']);

// mysql2: createPool, ? placeholders, [rows, fields] tuples
const [rows] = await mysqlPool.query('SELECT * FROM books WHERE genre = ?', ['Fiction']);

// mssql: request objects with named .input() bindings
const r = await pool.request().input('p1', 'Fiction')
  .query('SELECT * FROM books WHERE genre = @p1');

// oracledb: outFormat option, UPPERCASE column keys, manual connection release
const conn = await pool.getConnection();
const result = await conn.execute('SELECT * FROM books WHERE genre = :1', ['Fiction'],
  { outFormat: oracledb.OUT_FORMAT_OBJECT });
await conn.close();
```

**(@civitas-cerebrum/sql-客户端):**

```ts
// Same client, same call, any engine — detected from the connection string.
const db = new SqlClient({ connectionString: process.env.DB_URL! });
const res = await db.query('SELECT * FROM books WHERE genre = $1', ['Fiction']);

// Or fully portable: the builder renders engine-correct placeholders,
// quoting, and pagination from one chain.
const top = await QueryBuilder.select('books')
  .where('genre = ?', 'Fiction')
  .orderBy('price', 'desc')
  .limit(5)
  .run(db);
```

* * *

## \* 安装

安装软件包 **加引擎的驱动程序** - 驾驶员是 **可选同伴依赖关系**,因此您只拉取您使用的(没有自动安装,包括依赖于 sql-客户端的软件包):

```sh
# PostgreSQL
npm install @civitas-cerebrum/sql-client pg

# MySQL / MariaDB
npm install @civitas-cerebrum/sql-client mysql2

# SQLite (zero infrastructure)
npm install @civitas-cerebrum/sql-client better-sqlite3

# SQL Server
npm install @civitas-cerebrum/sql-client mssql

# Oracle (Thin mode — no Instant Client needed)
npm install @civitas-cerebrum/sql-client oracledb
```

要几个引擎吗? 将它们的驾驶员一起列出`npm install @civitas-cerebrum/sql-client pg mysql2`) (中文(简体) ). 导入软件包从不装入驱动程序; 是 `require`第一次使用时,一个缺失的投出一个清晰 `UnsupportedEngineException` 命名要安装的驱动程序.

**所需经费:** 节点.js 20.

| 发动机 | 连接字符串方案 | 驱动程序 | 占位符 | `returning()` |
| --- | --- | --- | --- | --- |
| **邮递SQL** | `postgres://` • 妇女 `postgresql://` | `pg` | `$1` | ✅ `RETURNING` |
| **MySQL / 马里亚DB** | `mysql://` • 妇女 `mariadb://` | `mysql2` | `?` | ❌ |
| **SQL 点** | `sqlite:` • 妇女 `sqlite::memory:` • 妇女 `file:` • 妇女 `:memory:` • 妇女 `*.db` • 妇女 `*.sqlite` | `better-sqlite3` | `?` | ✅ `RETURNING` |
| **SQL 服务器** | `mssql://` • 妇女 `sqlserver://` | `mssql` | `@p1` | ✅ `OUTPUT` |
| **甲骨文** | `oracle://` • 妇女 `oracledb://` | `oracledb` | `:1` | ❌ |

* * *

## QQ 快速启动

0基础设施 — 一个内存 SQLite 数据库, 没有 Docker, 没有服务器 。 复制复制并运行 :

```ts
import { SqlClient, QueryBuilder } from '@civitas-cerebrum/sql-client';

const db = new SqlClient({ engine: 'sqlite', connectionString: ':memory:' });

// Create a table (runScript splits a multi-statement script per engine)
await db.runScript(`
  CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, price REAL, in_stock INTEGER);
`);

// Insert (multi-row in one statement)
await QueryBuilder.insert('books')
  .values([
    { title: 'Dune', price: 12.99, in_stock: true },
    { title: '1984', price: 9.5,  in_stock: false },
  ])
  .run(db);

// Read straight into a typed, coercing ResultSet
const cheap = await QueryBuilder.select('books').where('price < ?', 10).fetch(db);
console.log(cheap.column('title'));            // ['1984']

const dune = await db.fetch('SELECT * FROM books WHERE title = ?', ['Dune']);
console.log(dune.one().number('price'));       // 12.99
console.log(dune.one().boolean('in_stock'));   // true  (1 → true)

await db.end();
```

* * *

## \* 特性

-   **引擎自动检测** - 从连接字符串方案推断出发动机;通过 `{ engine }` 只有在没有计划读取时才明确.
-   **可选、懒散的司机** – 驱动程序是可选的对等分解程序:只安装您使用的引擎;没有强迫您(或依赖于sql-客户端的软件包). 每个都是 `require`d 在第一次客户端构建, 和缺失一个扔 `UnsupportedEngineException` 命名要安装的驱动程序.
-   **总是瘫痪** —— Raw calls take a params range; 构建者需要 `?` 标记并让引擎正确的占位符样式。 无弦插入处.
-   **`sql` 标记模板** —— 说 `` sql`... ${value}` `` 安全地插入绑定参数,并编译到每个引擎正确的占位符样式; `sql.id()` 用于识别器,碎片巢.
-   **流利的查询构建器** —— 说 `select`编号 :`insert`编号 :`update`编号 :`delete` 带有引擎正确标识符的链条引用和页标( )`LIMIT/OFFSET` 对 `OFFSET..FETCH`),加 (中文(简体) ) `whereIn`编号 :`whereNull`编号 :`whereNotNull` 财务报告和审定财务报表 `count()`编号 :`exists()` 终端.
-   **一通电话读作:** —— 说 `fetch`编号 :`one`编号 :`maybeOne`编号 :`scalar` 直上陆地 `ResultSet`编号 :`Row` 从客户端或构建器链中找到 – 没有手动 `rows()` 包装.
-   **多行插图** —— 说 `values()` 接受一个数组(或重复调用),并编译一个单一多行语句.
-   **返回行写** —— 说 `returning(...cols)` 编译 `RETURNING` (邮递)或 `OUTPUT INSERTED./DELETED.` (msql);引擎在编译时没有等效的拒绝,有明显的错误.
-   **已打入、已规范的结果** - 每一个引擎的结果都被重塑成 `{ rows, rowCount, fields }`,在行型上通用; `Row<T>` 提供列名自动完成,同时仍然允许对大小写不敏感查找.
-   **不区分大小写、 类型强制存取器** —— 说 `rows(res).one().number('price')` 引擎是否返回 `PRICE` (甲骨文)或 `"12.99"` 作为字符串(postgres/mysql NUMERIC). 匹配者也比较数字,所以 `find({ price: 6.5 })` 匹配一个 `"6.50"` 电话.
-   **模拟行匹配者** —— 说 `where`编号 :`find` 接受文字、匹配者(`lt`, (中文(简体) ), `oneOf`, (中文(简体) ), `contains`),或全行上游; `where()` 返回可链条 `ResultSet`。 。 。 .
-   **交易** —— 说 `transaction(fn)` 自动 `COMMIT` 返回时 `ROLLBACK` 铸造者在五台发动机中运行`qb.run(tx)`) (中文(简体) ).
-   **脚本运行器** —— 说 `runScript()` 编号 : `splitSqlScript()` 拆分多语句 `.sql` 带有引擎意识规则的文件( Oracle) `/`, SQL 服务器 `GO`,评论/引文注意).
-   **连接失败** —— 说 `ping()` 准备状态检查和a `connectTimeoutMs` 选项,所以错误的主机会迅速死亡而不是被挂起.
-   **类型失败** —— 说 `QueryFailedException` 显示信件中的 SQL + 参数并携带 `.sql`, (中文(简体) ), `.params`驱动器 `.cause`;占位符式错误得到跨引擎提示.
-   **可插载发动机** —— 说 `registerEngineFactory()` 以自定义驱动器+方言对换.
-   **调试日志** —— 说 `DEBUG=sql:*` 用参数打印每个语句( 包括内部交易), 每个引擎命名空格(`sql:postgres`, (中文(简体) ), `sql:mysql`(中文(简体) ).

* * *

## 使用量: `SqlClient`

```ts
import { SqlClient } from '@civitas-cerebrum/sql-client';

// --- PostgreSQL (engine auto-detected from the URL scheme) ---
const pg = new SqlClient({ connectionString: 'postgres://user:pass@localhost:5432/mydb' });

// --- MySQL ---
const mysql = new SqlClient({ connectionString: 'mysql://user:pass@localhost:3306/mydb' });

// --- SQLite (in-memory, zero infra) ---
const sqlite = new SqlClient({ engine: 'sqlite', connectionString: ':memory:' });

// --- SQL Server (engine-native connection object) ---
const mssql = new SqlClient({
  engine: 'mssql',
  connection: { server: 'localhost', port: 1433, user: 'sa', password: 'Secret!1',
                database: 'mydb', options: { trustServerCertificate: true } },
});

// --- Oracle ---
const oracle = new SqlClient({ connectionString: 'oracle://user:pass@localhost:1521/FREEPDB1' });
```

连接会很快失败, 而不是挂在错误的主机上 – pass `connectTimeoutMs` 输入 配置并使用 `ping()` (例如在测试中) `globalSetup`核查前面的可达到性:

```ts
const db = new SqlClient({ connectionString: process.env.DB_URL!, connectTimeoutMs: 5000 });
await db.ping();   // throws if the engine is unreachable
```

### 原始查询

原始 SQL 中的占位符为 **引擎内置** (单位:千美元)`$1` 编号 : `?` 编号 : `@p1` 编号 : `:1` - 参见上表:

```ts
const { rows } = await pg.query('SELECT * FROM books WHERE genre = $1', ['Fiction']);
await pg.execute('UPDATE books SET stock = 0 WHERE book_id = $1', ['b1']);

// fetch() wraps the result in a ResultSet in one step
const book = (await pg.fetch('SELECT * FROM books WHERE book_id = $1', ['b1'])).one();
```

### `sql` 标记模板

喜欢 `sql` 便携式原始 SQL 标记: 插入在 引擎正确的占位符样式,所以相同的查询会随处可见。 使用 `sql.id()` 页:1 标识符(每个方言引用,从未参数化),并用巢片来作曲:

```ts
import { sql } from '@civitas-cerebrum/sql-client';

const genre = 'Fiction', max = 15;
await db.query(sql`SELECT * FROM books WHERE genre = ${genre} AND price < ${max}`);
// postgres → "... WHERE genre = $1 AND price < $2"; mysql → "... = ? AND < ?"; etc.

const onlyCheap = sql`AND price < ${10}`;
await db.query(sql`SELECT ${sql.id('title')} FROM books WHERE in_stock = ${true} ${onlyCheap}`);
```

### 交易

`transaction(fn)` 承诺返回和滚回抛出。 手柄带着方言,所以 `QueryBuilder.run(tx)` 在其中工作 `sql` 标记是只使用客户端的——在 tx 内部使用构建器 或原始 SQL 通过 `tx.query`:

```ts
await pg.transaction(async (tx) => {
  await QueryBuilder.update('books').set({ stock: 0 }).where('book_id = ?', 'b1').run(tx);
  await tx.execute('UPDATE books SET stock = stock - 1 WHERE book_id = $1', ['book-002']);
  // throw → automatic ROLLBACK; return → automatic COMMIT
});
```

完成时总是关闭客户端 :

```ts
await pg.end();
```

* * *

## QQ 查询构建器

便携式SQL从一个链- `?` 标记、引用和页码都按引擎编译:

```ts
import { QueryBuilder } from '@civitas-cerebrum/sql-client';

const top = await QueryBuilder.select('books')
  .columns('title', 'price')
  .where('price < ?', 15)
  .orderBy('price', 'desc')
  .limit(5)
  .run(pg);

// Multi-row INSERT — values() takes an array (or repeated calls)
await QueryBuilder.insert('books')
  .values([
    { book_id: 'b1', title: 'Dune' },
    { book_id: 'b2', title: '1984' },
  ])
  .run(pg);

// Writes can hand rows back: RETURNING (postgres/sqlite) or OUTPUT (mssql).
// mysql/oracle have no equivalent — returning() throws UnsupportedEngineException there.
const ins = await QueryBuilder.insert('books')
  .values({ title: 'Dune' })
  .returning('book_id', 'title')   // no args → all columns
  .run(pg);

// whereIn expands one placeholder per value (throws on an empty array, so you never compile
// `IN ()`); whereNull/whereNotNull avoid the `= NULL` footgun that silently matches nothing.
const some = await QueryBuilder.select('books')
  .whereIn('book_id', ['b1', 'b2', 'b3'])
  .whereNotNull('isbn')
  .fetch(pg);

// Inspect without executing — toSql() requires an explicit dialect
const { text, values } = QueryBuilder.select('books').where('price < ?', 15).toSql(pg.dialect);
```

**终端** 通过客户端发送查询,并将结果成一个调用:

```ts
await QueryBuilder.select('books').where('book_id = ?', 'b1').one(pg);        // Row (else ResultError)
await QueryBuilder.select('books').where('genre = ?', 'Fiction').fetch(pg);   // ResultSet
await QueryBuilder.select('books').where('book_id = ?', 'b1').maybeOne(pg);   // Row | undefined
await QueryBuilder.select('books').scalar(pg, 'title');                       // first cell
await QueryBuilder.select('books').where('price < ?', 10).count(pg);          // number
await QueryBuilder.select('books').where('book_id = ?', 'b1').exists(pg);     // boolean
```

可链: `columns(...cols)` • 妇女 `join(table, on)` • 妇女 `where(clause, ...params)` (多次电话拨打)- `whereIn(col, values)` • 妇女 `whereNull(col)` • 妇女 `whereNotNull(col)` • 妇女 `groupBy(...cols)` • 妇女 `having(clause, ...params)` • 妇女 `orderBy(col, dir)` • 妇女 `limit(n)` • 妇女 `offset(n)` • 妇女 `values(row | row[])` • 妇女 `set(row)` • 妇女 `returning(...cols)`。 。 。 . 终端 : `run(client)` • 妇女 `fetch(client)` • 妇女 `one(client)` • 妇女 `maybeOne(client)` • 妇女 `scalar(client, col?)` • 妇女 `count(client)` • 妇女 `exists(client)` • 妇女 `toSql(dialect)`。 。 。 .

> **说明:** SQL 服务器和甲骨文需要 `orderBy()` 开始时(`OFFSET..FETCH` 需要它吗. 要组队吗? 把它放入一个片段: `where('(genre = ? OR genre = ?)', a, b)`。 。 。 .

* * *

## 阅读结果

按名称从行中拉出值—— **不区分大小写** (甲骨文返回 UPPERCASE 密钥)和 **类型强制** (postgres/mysql 返回 NUMERIC/DECIMAL 作为字符串):

```ts
import { rows, getNumber } from '@civitas-cerebrum/sql-client';

const res = await db.query('SELECT * FROM books WHERE book_id = $1', ['book-001']);

// Fluent wrapper
const book = rows(res).one();                 // exactly one row, else throws ResultError
book.string('title');                         // 'To Kill a Mockingbird'
book.number('price');                         // 12.99
book.boolean('in_stock');                     // normalizes 1/0, 't'/'f', 'true'/'false'

rows(await db.query('SELECT count(*) FROM books')).scalar();   // first cell of first row
rows(res).column('title');                    // every title
rows(res).find({ genre: 'Fiction' });         // first matching row, or undefined
rows(res).maybeOne();                         // 0..1 rows, else throws ResultError

// Or standalone functions on a raw row
getNumber(res.rows[0], 'price');              // 12.99
```

语义学:缺栏- `undefined`; SQL NULL → `null`;否则被胁迫.

### 过滤行

`where`编号 :`find` 接受部分(栏-正文) **或者说** )或全行上游:

```ts
import { rows, lt, oneOf, contains } from '@civitas-cerebrum/sql-client';

const res = await db.query('SELECT * FROM books');
rows(res).where({ genre: 'Fiction', price: lt(10) });        // matchers
rows(res).find({ title: contains('1984') });
rows(res).where(row => row.number('price')! < 10);           // predicate

// where() returns a ResultSet, so filtering keeps chaining:
rows(res).where({ genre: 'Fiction' }).column('title');
rows(res).where({ book_id: 'b1' }).one();
if (rows(res).where({ status: 'SOLD' }).isEmpty()) { /* none sold */ }
rows(res).where({ genre: 'Fiction' }).all().forEach(r => { /* Row[] */ });

// standalone, on raw rows
import { filterRows } from '@civitas-cerebrum/sql-client';
filterRows(res.rows, { status: oneOf(['ACTIVE', 'SOLD']) });
```

匹配者 : `eq ne lt lte gt gte between oneOf like contains startsWith endsWith matches isNull notNull not`。 。 。 . 无效/ 未定义单元格只匹配 `isNull()`; 比较/字符串匹配器返回 `false` 对于它。 平等 匹配者(`eq`编号 :`ne`编号 :`oneOf`)和字面部分在双方都是数字时进行数字比较,所以 `6.5` 查询与 `"6.50"` 由 postgres/ misql 返回的单元格.

* * *

## API 参考

### 🔌 `SqlClient`

| 成员 | 说明 |
| --- | --- |
| `new SqlClient(config)` | `config`数字 : `{ connectionString?, engine?, connection?, max?, connectTimeoutMs?, dialect? }` |
| `query<T>(sql, params?)` 编号 : `query<T>(fragment)` | 参数化查询(字符串或 `sql` ∗∗∗∗∗ `Promise<SqlResult<T>>` |
| `execute(sql, params?)` 编号 : `execute(fragment)` | 书面陈述 – `Promise<SqlResult>` (受影响行的行数) |
| `fetch<T>(sql, params?)` | `query()` 接下来 `rows()` → `Promise<ResultSet<T>>` |
| `transaction(fn)` | 原子块; `fn(tx)` 获得 `tx.query`编号 :`tx.execute` **加号** `tx.dialect`编号 :`tx.engine`这样 `QueryBuilder.run(tx)` 工作内容 |
| `runScript(sqlText)` | 拆分一个多语句脚本( 引擎意识) 并按顺序执行 |
| `ping()` | 廉价连接探测器; 如果引擎无法到达则丢弃 |
| `end()` | 关闭池/连接 |
| `engine` 编号 : `dialect` | 解决的引擎名称和方言实例 |

### 📊 `SqlResult<T>`

`{ rows: T[], rowCount: number, fields: { name, dataTypeID }[] }` ——每个引擎产生的正态形状.

### 📖 `ResultSet` 编号 : `Row`

| 成员 | 说明 |
| --- | --- |
| `rows<T>(result | rawRows)` | 将结果( 或原始数组) 包入 a `ResultSet<T>` |
| `.one()` 编号 : `.maybeOne()` | 完全1/在 - 最一行,否则 `ResultError` |
| `.first()` 编号 : `.at(i)` | `Row` 或者说 `undefined` |
| `.scalar(column?)` | 第一行的第一个单元格 |
| `.column(name)` | 所有行各一栏 |
| `.find(where)` | 第一个匹配 `Row` (部分字数/对等,或 `(row) => boolean`(中文(简体) ) |
| `.where(where)` | 过滤器 → 可链条 `ResultSet` (相同的部分/预选表格) |
| `.map(fn)` 编号 : `.all()` 编号 : `.raw()` | 逐行绘制地图/ 全部覆盖为 `Row[]` / 打开到生行 |
| `Row<T>.get/string/number/boolean(col)` | 大小写不敏感、类型强制的单元格访问; `T` 给列名自动完成 |
| `Row.has(col)` 编号 : `Row.raw()` | 存在检查/ 内在对象 |

### 🏷️ `sql` 标记脚本( c)

| 导出 | 说明 |
| --- | --- |
| `` sql`...${v}` `` | 构建一个 `SqlFragment`; 插件 ; 捆绑的活体; 筑巢的碎片 |
| `sql.id(name)` | 插入一个标识符( 引号, 不参数化) |
| `splitSqlScript(sqlText, engine?)` | 将多声明脚本拆分为单个声明( 引擎意识) |

### · 例外情况

| 类 | 掷出时 | 额外 |
| --- | --- | --- |
| `SqlException` | 下方所有东西的基类 | —— 说 |
| `QueryFailedException` | 查询/执行失败 | 消息包括 SQL + params(+ 占位符提示); `.sql`, (中文(简体) ), `.params`, (中文(简体) ), `.cause` |
| `UnsupportedEngineException` | 未知引擎、 缺少驱动程序或不支持 `returning()` | —— 说 |
| `ResultError` | `one()`编号 :`maybeOne()` 基本要求或失败的胁迫 | —— 说 |

### 扩展

| 函数 | 说明 |
| --- | --- |
| `registerEngineFactory(engine, factory)` | 插入自定义 `{ createDriver, createDialect }` 组合 |
| `detectEngine(connectionString)` | 用于检测 `SqlClient` |
| `Dialect` | 执行 `placeholder`, (中文(简体) ), `quoteIdentifier`, (中文(简体) ), `compilePagination`, (中文(简体) ), `compileReturning` |
| `createLogger(ns)` 编号 : `log` | `debug`\- 基于记录器(`SqlLogger`在 `sql:*` 命名空间 |

* * *

## 测试工作流程

```sh
# Light (unit + zero-infra SQLite) — no Docker needed
npm test

# Single engine (bring up just that engine's container)
docker compose --profile postgres up -d --wait && npm run test:postgres
docker compose --profile mysql    up -d --wait && npm run test:mysql
docker compose --profile mssql    up -d --wait && npm run test:mssql
docker compose --profile oracle   up -d --wait && npm run test:oracle

# Full matrix — all five live engines
docker compose --profile all up -d --wait
npm run test:all-engines
docker compose --profile all down -v
```

以上命令用于局部迭代. **每个公关都由CI负责**: 节点24上的单元 + SQLite 套房, _财务报告和审定财务报表_ 完整的四引擎活矩阵(Postgres, MySQL, SQL Server, Oracle)——每个引擎在自己的孤立容器中——所以在合并前会捕捉到引擎特有的回归.

主机端口与已经运行的东西相撞? 覆盖它们: `SQL_CLIENT_PG_PORT`, (中文(简体) ), `SQL_CLIENT_MYSQL_PORT`, (中文(简体) ), `SQL_CLIENT_MSSQL_PORT`, (中文(简体) ), `SQL_CLIENT_ORACLE_PORT` (并指所取所取所取所取所取所取等. `SQL_TEST_URL=postgres://bookhive:bookhive@localhost:15432/bookhive npm run test:postgres`) (中文(简体) ).

调试日志 : `DEBUG=sql:* npm test` (每个引擎命名空间) `sql:postgres`, (中文(简体) ), `sql:mysql`, (中文(简体) ), `sql:sqlite`, (中文(简体) ), `sql:mssql`, (中文(简体) ), `sql:oracle`;涵盖交易报表和生命周期).

* * *

## 发动机陷阱

跨引擎不同 包吸收你- 少数不能:

-   **原始 SQL 的占位符样式不同** —— 说 `$1` (邮递员) `?` · (misql/sqlite) - `@p1` · (msql) · `:1` (甲骨文). 使用构建器或 `sql` 标记可移植代码; 两者都使正确的样式.
-   **Oracle 返回 UPPERCASE 列键** —— 说 `Row` 访问者不敏感,所以 `row.string('title')` 不管怎么样,都行 仅生 `res.rows[i].title` 索引受到影响.
-   **NUMERIC/DECIMAL 作为字符串到达** 邮递/邮件 `row.number()` 强迫,和匹配者比较数字,所以你很少注意到。 原始的细胞访问并不强制.
-   **SQL 服务器 `UNIQUE` 将 NULLs 视为重复** 第二行为: `NULL` 在一个 `UNIQUE` 列违反了对msql(不同于其他引擎)的限制。 在测试固定装置中使用不同的监视值 .
-   **SQL 组合键 服务器按位置绑定参数** —— 说 `@p1..@pN` 映射到参数阵列的顺序; 构建者和 `sql` 标记自动处理.
-   **SQLite 没有本地日期/ 布尔** - 驱动程序正常 `Date → ISO string`, (中文(简体) ), `boolean → 1/0`, (中文(简体) ), `undefined → null` 因此,相同的固定代码 工作与其他引擎.

* * *

## 非目标

故意超出范围保持精干和可预期. 为此,可使用专用工具:

-   **ORM / 关系/ 实体映射** 这是查询客户端 不是ORM.
-   **移徙和计划管理** 使用你的移徙工具; `runScript()` 只执行您给出的 SQL .
-   **正在流出/ 光标** 对于庞大的成果组合——成果是实实在在的数组.
-   **命名参数** —— 仅定位(建造者 `?` 编辑 `sql` 标记覆盖人类学差距.
-   **或组合的建树** - 单人内部的表达式 OR `where()` 换成碎片.
-   **提升助手** ——特写引擎 `ON CONFLICT` 编号 : `MERGE` 现在直接使用 SQL.

* * *

## 克劳德代码技能

货包船a [克劳德代码](https://claude.com/claude-code) 技术(`skills/sql-client/SKILL.md`(中文(简体) ) 它教毒剂 完整的API表面。 它自动安装在 `npm install` 改为 `~/.claude/skills/` 全球安装`-g`),或仅此 `<project>/.claude/skills/` 对于本地的( 当工程根没有时从未写入) `package.json`)——包是直接安装还是到达 穿过另一个 `@civitas-cerebrum` 软件包。 在使用步骤-API测试的项目中 技术将测试光谱数据库的工作推迟到 `database-testing` 技术.

跳过副本——例如,在CI或容器中——设置 `SQL_CLIENT_SKIP_SKILLS=1` 在安装前.

## 常见问题

### @civitas-cerebrum/sql-client 是什么？

一个轻量级多引擎的SQL客户端\(Postgres, MySQL, SQLite, SQL Server, Oracle\),拥有流畅的查询构建器,打出结果,以及被默认的查询\.

### 如何安装 @civitas-cerebrum/sql-client？

在项目目录运行 npm install @civitas-cerebrum/sql-client，或使用 pnpm add / yarn add / bun add。

### @civitas-cerebrum/sql-client 支持 TypeScript 吗？

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

### @civitas-cerebrum/sql-client 支持 ESM 还是 CommonJS？

以 CommonJS 形式发布，可以直接 require\(\)；在 ES Module 项目中也可以用 import 引入（Node\.js 会把 module\.exports 作为默认导出）。

### @civitas-cerebrum/sql-client 需要什么版本的 Node\.js？

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

### @civitas-cerebrum/sql-client 可以商用吗？

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

### @civitas-cerebrum/sql-client 的下载量有多少？

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

### @civitas-cerebrum/sql-client 还在维护吗？

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

