子查询可以得到一组对象或一个标量,在主查询里用 in、exists 做集合判断,或用 detached 做不受当前上下文限制的查询;没有链接时也可以用属性关联到另一类对象。
在 with 里写 (select 对象类型 filter 条件) 得到一组对象,主查询里用 filter .链接 in 变量 表示「当前对象的某链接指向该集合中的任意一个」。
示例:查「至少有 2 行订单行」的订单头下的所有订单行(即只返回属于这类订单头的行)。
with order_list := (
select Order
filter count(.<order[is OrderLine]) >= 2
)
select OrderLine {
line_no,
product_name,
qty,
price,
order: { order_no, order_date }
}
filter .order in order_list
结果示意:仅返回「订单行数 ≥ 2」的订单下的订单行。
[
{ "line_no": 1, "product_name": "商品A", "qty": 2, "price": 50.00, "order": { "order_no": "ORD001", "order_date": "2023-10-01" } },
{ "line_no": 2, "product_name": "商品B", "qty": 1, "price": 99.00, "order": { "order_no": "ORD001", "order_date": "2023-10-01" } }
]
写在 select 里的子查询默认受「当前行」的上下文影响。若需要不受当前查询范围限制的结果(例如在每条订单旁显示「订单总条数」),可用 detached。
count(Order) 在「每条订单一行」的上下文中,可能只计到当前对象。
count(detached Order) 表示在全局计订单总数。
示例:查每条订单的订单号、日期,并带出订单总条数。
with total := count(detached Order)
select Order {
order_no,
order_date,
total_count := total
}
结果示意:
[
{ "order_no": "ORD001", "order_date": "2023-10-01", "total_count": 5 },
{ "order_no": "ORD002", "order_date": "2023-10-02", "total_count": 5 }
]
对比:若写 count := count(Order) 而不使用 detached,结果可能受上下文影响,不是全局总数。
两类对象没有直接建链接时,可以通过属性相等关联(例如业务表存了外部系统单号)。用子查询根据当前行的属性去匹配另一类对象并取字段。
示例:订单头有属性 approval_no 存审批单号,审批对象 Approval 有 approval_no、title。在订单结果中带出审批单标题。
select Order {
order_no,
order_date,
approval_title := (
select Approval
filter .approval_no = Order.approval_no
).title
}
结果示意:
[
{ "order_no": "ORD001", "order_date": "2023-10-01", "approval_title": "2023年10月审批单" },
{ "order_no": "ORD002", "order_date": "2023-10-02", "approval_title": null }
]
当没有匹配的审批单时,子查询结果为空,对应字段为 null。
这是一个高频易错点:在 DeepQL 中,子查询(select ... filter ...)如果要用在以下场景,必须用圆括号 () 包裹,否则会语法报错或语义错误。
在 with 中将子查询赋值给变量时,子查询要套一层圆括号:
-- ✅ 正确:子查询用圆括号包裹
with big_orders := (
select Order filter .status = 'confirmed'
)
select big_orders { order_no, order_date }
-- ❌ 错误:缺少圆括号
with big_orders := select Order filter .status = 'confirmed'
select big_orders { order_no, order_date }
将子查询作为 count、sum、array_agg 等函数的参数时,同样需要圆括号——注意此时会出现双层括号(外层是函数调用的括号,内层是子查询的括号),不能省略:
-- ✅ 正确:双层括号,外层是 count() 的,内层是子查询的
select Order {
order_no,
high_value_lines := count((
select .<order[is OrderLine]
filter .qty * .price > 100
))
}
-- ❌ 错误:只有一层括号,子查询没有被正确包裹
select Order {
order_no,
high_value_lines := count(
select .<order[is OrderLine]
filter .qty * .price > 100
)
}
在 select 形状的计算字段中写子查询也一样——只要不是在 with 中独立占一行,都需要圆括号:
select Order {
order_no,
-- ✅ 正确
latest_line := (
select .<order[is OrderLine]
order by .line_no desc
limit 1
) { product_name, qty }
}
记忆口诀:子查询只要不是「顶层语句」,就要套 ()。赋值套、嵌套套、传参套——宁可多套一层,不能少。
在 select 的形状中引用一个已有链接时,有两种写法——直接展开链接和用子查询重新 select 链接。它们看似相似,实际语义和能力完全不同。
以订单头的 customer 链接为例:
-- 写法 A:直接展开链接(shape 语法)
select Order {
order_no,
customer: {
code,
name_zh := <str>.name['zh-cn']
}
}
-- 写法 B:子查询重新 select 链接
select Order {
order_no,
customer_info := .customer {
code,
name_zh := <str>.name['zh-cn']
}
}
两者结果完全一样——都是展开 customer 的 code 和中文名。那什么时候必须用写法 B?
直接展开链接(写法 A)只能指定要取哪些字段,不能对链接目标做过滤、排序、限制数量。如果需要这些能力,就必须用子查询形式(写法 B)。
场景:订单行只取前 3 条高价的
-- ❌ 写法 A 做不到:链接直接展开不支持 filter / order / limit
select Order {
order_no,
.<order[is OrderLine]: { -- 只能列字段,不能加 filter
line_no,
product_name,
}
}
-- ✅ 写法 B:子查询形式,可以自由 filter / order / limit
select Order {
order_no,
top_lines := (
select .<order[is OrderLine]
filter .qty * .price > 50
order by .qty * .price desc
limit 3
) {
line_no,
product_name,
qty,
price,
}
}
场景:只展开状态为 confirmed 的客户(multi link 场景下过滤)
-- 假设 Order.customers 是 multi link
select Order {
order_no,
-- 直接展开:返回所有关联客户
customers: { code, name_zh := <str>.name['zh-cn'] },
-- 子查询:只返回 code 以 'VIP' 开头的客户
vip_customers := (
select .customers
filter .code like 'VIP%'
) { code, name_zh := <str>.name['zh-cn'] }
}
|
直接展开 |
子查询 | |
|---|---|---|
|
语法 |
|
|
|
能否 filter |
❌ 不可以 |
✅ 可以 |
|
能否 order by |
❌ 不可以 |
✅ 可以 |
|
能否 limit |
❌ 不可以 |
✅ 可以 |
|
字段名 |
与链接同名 |
可以起新别名(如 |
|
适用场景 |
简单展开,取全量关联数据 |
需要对关联数据做筛选、排序、截取 |
简单记:直接展开 = 「把关联对象的哪些字段带出来」;子查询重取 = 「对关联对象先做一轮 select,再带出字段」。后者多了一层查询能力,但也需要多写子查询和圆括号。
DeepQL 是完全可组合的:任何查询表达式都可以嵌入到另一个查询中,不限制嵌套深度。例如在 select 的计算字段里再写 select + filter,或在 with 里嵌套 with,这些都是合法的。
示例:查每个订单,带出「金额最高的一条订单行」的商品名称。
select Order {
order_no,
top_product := (
select .<order[is OrderLine] { product_name }
order by .qty * .price desc
limit 1
).product_name
}
提示:如果反向链接的使用频率很高,可以将其在对象的 schema 中定义为计算链接,后续查询就可以像正向链接一样直接用,不必每次都写反向链接语法。
|
用法 |
说明 |
|---|---|
|
with 中 select 得到集合,主查询中 |
只保留「关联对象在集合内」的记录 |
|
|
子查询在全局执行,不受当前行/当前 select 范围限制 |
|
子查询中 |
无链接时通过属性关联到另一类对象并取字段 |
|
子查询必须加 |
赋值变量、传入函数、内联计算字段时,子查询都要用圆括号包裹 |
|
|
直接展开只能选字段;子查询形式可以 filter / order / limit |
|
任意嵌套 |
DeepQL 完全可组合,select 内可嵌入 select + filter + order + limit |
下一步可以按维度分组并做聚合,见「六、分组与汇总」。
回到顶部
咨询热线
