根查询与取形
所有查询从 sqlClient.createQuery(model, (q, table) => …) 开始。回调里拿到查询构建器 q 与表对象 table,用链式调用描述查询,最后 select 定形状、收尾方法执行。
// ① 模型与视图(与快速上手章一致,示例数据可直接运行)
import { model, prop, dto } from '@ts-grm/core'
import { sqlClient } from './infra/sql-client'
const Book = model("Book", "id", class {
id = prop.i64()
name = prop.str(50)
edition = prop.i32()
price = prop.num(10, 2)
})
const BookView = dto.view(Book, c => [
c.id,
c.name,
c.price,
])
// ② 根查询:where/orderBy 可省略,select 决定形状
const rows = await sqlClient.createQuery(Book, (q, book) => {
return q.select(book.fetch(BookView)) // 形状 = 视图
}).fetchList()
console.log(rows)
select 的三种形态
| 形态 | 写法 | 结果 |
|---|---|---|
| 视图取形 | q.select(book.fetch(View)) | 按视图的精确 DTO,最常用 |
| 映射投影 | q.select({ book, rank }) | 自定义键名/混合多段表达式(rank 可以是原生 SQL、公式等) |
| 聚合投影 | q.select(dsl.count()) / q.select(book.name, dsl.max(book.edition)) | 聚合结果(配合 groupBy,见「聚合与分组」) |
映射投影示例(键名自定义,可混入列表达式):
// ③ 映射投影:book 视图 + 价格列,键名自定
const rows2 = await sqlClient.createQuery(Book, (q, book) => {
return q.select({
item: book.fetch(BookView),
price: book.price,
})
}).fetchList()
console.log(rows2.map(r => `${r.item.name.padEnd(28)} → ${r.price}`).join('\n'))
收尾方法:按"想要几行"选择
创建查询对象后,用一个收尾方法执行:
| 方法 | 语义 | 返回 |
|---|---|---|
fetchList() | 多行 | T[] |
fetchRequired() | 恰好一行:0 行 NoDataError、多行 TooManyDataError | T |
fetchOptional() | 至多一行:无行返回 null({ nullAsUndefined: true } 时 undefined) | T | null |
fetchCount() | 行数 | number |
例:读某本书(不存在时返回 null):
// ④ fetchOptional:至多一行
const maybe = await sqlClient.createQuery(Book, (q, book) => {
q.where(book.id.eq(99)) // 不存在
return q.select(book.fetch(BookView))
}).fetchOptional()
console.log('id=99 →', maybe) // null
没有 .fetchOne()——"必须有且仅有一行"用 fetchRequired();"至多一行"用 fetchOptional()。
查询是延迟构建的
createQuery(...) 本身不执行:只有调用收尾方法才真正生成 SQL 并执行(这也是 createQuery 能作为工厂被复用/传给子查询、计算属性等的原因)。
下一步
查询骨架有了,下一节是条件表达式的全部武器:比较、区间、IN、模糊与动态条件。