子查询
dsl.subQuery(model, (q, table) => …) 与根查询同样形态,但不取回数据——它作为表达式嵌进外层查询(IN、EXISTS 等)。
标量子查询:inSubQuery
// ① 模型(书/作者 m2m,示例数据含 author 表,完整可运行版见工作区)
import { model, prop, dto, dsl } from '@ts-grm/core'
import { sqlClient } from './infra/sql-client'
const Author = model("Author", "id", class {
id = prop.i64()
name = prop.embedded({
firstName: prop.str(50),
lastName: prop.str(50),
})
books = prop.m2m(Book).mappedBy("authors")
})
const Book = model("Book", "id", class {
id = prop.i64()
name = prop.str(50)
edition = prop.i32()
price = prop.num(10, 2)
authors = prop.m2m(Author).joinTable({
name: "book_author_mapping",
joinThisColumns: ["book_id"],
joinTargetColumns: ["author_id"],
})
})
const BookView = dto.view(Book, c => [c.id, c.name])
// ② 标量 IN:作者 Ada 写的书
const rows = await sqlClient.createQuery(Book, (q, book) => {
q.where(book.id.inSubQuery(
dsl.subQuery(Book, (q2, b2) => {
// 从书-作者映射里挑出属于 Ada 的书——SQL 里是 IN (select ...)
return q2.select(dsl.associationModel(Book, "authors").sourceId)
})
))
return q.select(book.fetch(BookView))
}).fetchList()
console.log(rows)
外层列 .inSubQuery(sub) 是最常见的形态;同族还有 eq/ne/gt...SubQuery(标量比较子查询)。
元组子查询:多列 IN
dsl.tuple(a, b).inSubQuery(...) 用复合键匹配:
q.where(
dsl.tuple(order.id.x, order.id.y.a).inSubQuery(
dsl.subQuery(ORDER_ITEM, (q, item) => {
return q.select(item.orderIdX, item.orderIdYA) // 列数必须与元组一致
})
)
)
关联模型子查询
dsl.associationModel(模型, "关联属性") 把关联当作子查询的"表"——直接站在关联上过滤:
// ③ 关联模型子查询 + notExists:没有作者的书(反例:把 = 111 的行代入)
const lonely = await sqlClient.createQuery(Book, (q, book) => {
q.where(
dsl.notExists(
dsl.subQuery(dsl.associationModel(Book, "authors"), (q2, asso) => {
q2.where(asso.sourceId.eq(book.id)) // 关联回当前书
})
)
)
return q.select(book.fetch(BookView))
}).fetchList()
console.log(lonely) // 种子数据:3 本书都有作者 → []
asso.sourceId(Book→authors 关联的源侧 id);关联集合/键的更多字段可查「关联参考」;dsl.exists(...)是 notExists 的镜像。
注意事项
- 子查询投影的列数/类型必须与用法匹配(元组 IN 尤其);
- 子查询回调里可以
q.where关联外层(asso.sourceId.eq(book.id))——相关子查询,SQL 里生成EXISTS(select ... where source_id = outer.id); - 子查询单独列出来也是「根查询」形态,几乎都能独立 run(便于调试)。
下一步
子查询、CTE、集合运算是"多段 SQL"的组合工具。下一节:CTE 与集合运算——unionAll 拼表与 WITH 公用表达式。