资讯动态

drizzle-orm 0.20.0 变更详解:WITH 子句(CTE)支持、子查询修复与 `.as()` / `sql<type>` 语法迁移

发布时间:2026/9/19 21:45:13 来源:尧图企业网站定制
drizzle-orm 0.20.0 变更详解WITH 子句CTE支持、子查询修复与.as()/sqltype语法迁移【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm本文围绕 drizzle-orm 0.20.0 版本的发布说明展开系统讲解该版本引入的 WITH 子句Common Table Expression公共表表达式支持、子查询选择与连接相关 Bug 的修复以及两处破坏性 API 更名.subquery(alias)→.as(alias)、sqlquery.astype()→sqltypequery()。读完本文你将掌握如何在 drizzle-orm 中定义可复用的 CTE 并用于分组统计查询同时理解新语法与旧语法之间的迁移路径。本文内容以 changelogs/drizzle-orm/0.20.0.md 为主体并辅以 drizzle-orm/src/pg-core/db.ts、drizzle-orm/src/pg-core/subquery.ts、drizzle-orm/src/subquery.ts、drizzle-orm/src/sql/sql.ts 等源码佐证。版本背景drizzle-orm 0.20.0 是 drizzle-orm 0.20.x 系列的首个正式版本。该版本的核心工作集中在查询构建能力的完善上主要包含三类变更 新增 WITH 子句CTE支持配套示例可直接迁移到实际项目中 修复了子查询subquery在 select / join 场景下的多处 Bug❗ 两处 API 更名与语法调整.subquery(alias)→.as(alias)sqlquery.astype()→sqltypequery()属于破坏性变更虽然旧语法仍可用但已被标记为弃用。其中 WITH 子句支持是本次版本的最大亮点下文将重点展开。WITH 子句CTE支持官方示例先定义子查询再在主查询中使用0.20.0 的发布说明给出了 WITH 子句的完整用法示例const sq db .select() .from(users) .prepareWithSubquery(sq); const result await db .with(sq) .select({ id: sq.id, name: sq.name, total: sqlnumbercount(${sq.id})::int(), }) .from(sq) .groupBy(sq.id, sq.name);这段代码分两步完成先构建子查询db.select().from(users).prepareWithSubquery(sq)将一段常规的 SELECT 查询包装成一个带有别名sq的子查询对象再作为 CTE 引入主查询db.with(sq)把该子查询注册为 WITH 子句随后.select({...}).from(sq)从该 CTE 中选取字段并通过groupBy进行分组聚合。其中sqlnumbercount(${sq.id})::int()是带类型参数的 SQL 模板表达式——sqlnumber表明该表达式在类型层面返回number运行时生成count(sq.id)::int的 PostgreSQL SQL 片段用于对sq.id进行计数并转为整数。从源码看 CTE 的实现机制从当前仓库源码来看prepareWithSubquery方法已经演进为更通用、更现代化的$with构建器其核心类型定义位于 drizzle-orm/src/pg-core/subquery.tsexport interface WithBuilder { TAlias extends string(alias: TAlias): { as: { TSelection extends ColumnsSelection( qb: TypedQueryBuilderTSelection | ((qb: QueryBuilder) TypedQueryBuilderTSelection), ): WithSubqueryWithSelectionTSelection, TAlias; ( qb: TypedQueryBuilderundefined | ((qb: QueryBuilder) TypedQueryBuilderundefined), ): WithSubqueryWithoutSelectionTAlias; }; }; TAlias extends string, TSelection extends ColumnsSelection(alias: TAlias, selection: TSelection): { as: (qb: SQL | ((qb: QueryBuilder) SQL)) WithSubqueryWithSelectionTSelection, TAlias; }; }对应的运行时方法在 drizzle-orm/src/pg-core/db.ts 中实现$with: WithBuilder (alias: string, selection?: ColumnsSelection) { // 创建 WithBuilder 实例省略内部细节 }; with(...queries: WithSubquery[]) { // 将 CTE 列表注入主查询的 withList随后构建 SELECT withList: queries, // ... }其类型系统由 drizzle-orm/src/subquery.ts 中的Subquery与WithSubquery两个类支撑export class SubqueryTAlias, TSelectedFields implements SQLWrapper { declare _: { brand: Subquery; sql: SQL; selectedFields: TSelectedFields; alias: TAlias; isWith: boolean; usedTables?: string[]; }; constructor(sql: SQL, fields: TSelectedFields, alias: string, isWith false, usedTables: string[] []) { /* ... */ } } export class WithSubqueryTAlias, TSelection extends SubqueryTAlias, TSelection { static override readonly [entityKind]: string WithSubquery; }从结构上可以推断WithSubquery是Subquery的特化子类通过isWith标志与普通子查询区分而SQLWrapper接口定义于 drizzle-orm/src/sql/sql.ts任何实现getSQL()方法的对象都可作为 SQL 片段参与拼接是它们能被嵌入db.with()与db.select()的类型基础。因此 CTE 本质上是一个可复用、带别名、可被引用列的子查询对象这正是它与普通内联子查询的差别所在。当前推荐写法$withas0.20.0 之后的演进版本中CTE 的标准写法统一为db.$with(alias).as(queryBuilder)用法如下来自 drizzle-orm/src/pg-core/db.ts 的 JSDoc 示例// 方式一传入完整的查询构建器 const sq db.$with(sq).as( db.select().from(users).where(eq(users.id, 42)), ); // 方式二传入 SQL 表达式并显式声明返回列 const sq db.$with(sq).as( db.select({ name: sqlstringupper(${users.name}).as(name), }).from(users), ); // 在主查询中引用 CTE const result await db.with(sq).select({ name: sq.name }).from(sq);$with的第一个参数是 CTE 别名对应 SQL 中WITH sq AS (...)的sq当第二个参数传入列选择selection时返回的WithSubquery会带有完整列类型便于在主查询中做类型安全的字段引用。drizzle-orm 的pg-core、mysql-core、sqlite-core、singlestore-core、gel-core各驱动目录下均有对应的subquery.ts/db.ts/query-builder.ts实现说明 WITH 子句是跨数据库方言的通用能力。子查询选择与连接的 Bug 修复0.20.0 同时修复了 various bugs with selecting/joining of subqueries子查询在选择列与 join 时的多个 Bug。这些修复针对的是子查询在以下两类场景中的行为select 子查询列从子查询中选取字段时别名与列名的映射、类型推导可能出现偏差join 子查询将子查询作为连接目标如.innerJoin(sq, ...)/.leftJoin(sq, ...)时生成的 SQL 拼接与列引用可能不正确。从 drizzle-orm/src/sql/sql.ts 中可以看到Subquery.prototype.getSQL被显式挂载子查询对象能够直接作为 SQL 片段参与查询构建而AddAliasToSelection这类类型工具在 drizzle-orm/src/pg-core/subquery.ts 中被引用负责把别名注入所选字段的类型签名。0.20.0 的修复正是围绕这些别名注入与拼接环节展开的修复后子查询在 select / join 中的字段引用与 SQL 生成更加稳定。对于使用方而言如果此前在 0.19.x 中遇到子查询选择列错位或 join 报错的问题升级到 0.20.0 后应可直接获得修复。破坏性变更两处 API 迁移.subquery(alias)更名为.as(alias)旧写法const sq db.select().from(users).subquery(sq);新写法const sq db.select().from(users).as(sq);as是 SQL 生态中的通用语义对应SELECT ... AS aliasdrizzle-orm 借此统一了子查询与普通列的别名命名方式。在 drizzle-orm/src/sql/sql.ts 的SQL.as()实现中可以看到as接受一个别名并返回SQL.Aliased包装对象as(alias?: string): SQLT | SQL.AliasedT { if (alias undefined) { return this; } return new SQL.Aliased(this, alias); }这说明.as()是贯穿列表达式别名与子查询别名的统一入口。注意0.20.0 发布说明中明确该更名被标记为 ❗意味着旧方法.subquery(alias)会被移除升级时需要全局替换。sqlquery.astype()改为sqltypequery()旧写法已弃用sqlselect 1.asnumber()新写法sqlnumberselect 1()两种写法的语义相同都表示该 SQL 片段在类型层面返回number。新语法把类型参数直接放在模板标签的尖括号中更符合 TypeScript 泛型标签函数的直觉。旧语法在 drizzle-orm/src/sql/sql.ts 中保留为标记deprecated的重载源码注释明确提示迁移方向/** * deprecated * Use sqlDataTypequery.as(alias) instead. */ asTData(): SQLTData;也就是说旧语法虽然暂时还能编译运行但按发布说明所述will be removed in one of the next releases将在后续版本中移除建议在升级到 0.20.0 时一并完成迁移避免后续版本升级时出现编译错误。迁移清单旧语法0.19.x新语法0.20.0状态qb.subquery(alias)qb.as(alias)破坏性变更旧 API 被移除sqlquery.asType()sqlTypequery()破坏性变更旧语法保留但已弃用db.select()...prepareWithSubquery(alias)db.$with(alias).as(qb)演进写法0.20.0 引入 WITH 支持后续演进为$with升级建议逐项替换弃用 API使用仓库内全局搜索如grep -rn \.subquery(与grep -rn \.as定位所有旧写法调用点统一迁移到.as()与sqlType新语法验证子查询场景升级后重点回归涉及子查询 select / join 的查询确认 0.20.0 的修复未引入新的行为变化尝试 WITH 子句对于先筛选再聚合的多步查询优先用db.$with(alias).as(...)db.with(...)重构既能提升 SQL 可读性又能让类型推导贯穿 CTE 内外。总结drizzle-orm 0.20.0 通过 WITH 子句支持补齐了 CTE 查询能力配合.as()与sqlType新语法形成了更统一、更类型安全的子查询与 SQL 表达式体系。db.$with(alias).as(qb)与db.with(...)的组合其实现位于 drizzle-orm/src/pg-core/db.ts、drizzle-orm/src/pg-core/subquery.ts是当前版本及后续版本中处理 WITH 子句的标准姿势而两处破坏性变更也提醒使用者尽早完成语法迁移避免被后续版本强制升级。【免费下载链接】drizzle-ormORM项目地址: https://gitcode.com/gh_mirrors/dr/drizzle-orm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价