全部文档
文档中心DeepModel功能DeepQL常用语法与使用技巧五、子查询与集合关系

五、子查询与集合关系

子查询可以得到一组对象或一个标量,在主查询里用 inexists 做集合判断,或用 detached 做不受当前上下文限制的查询;没有链接时也可以用属性关联到另一类对象。

在 with 里写 (select 对象类型 filter 条件) 得到一组对象,主查询里用 filter .链接 in 变量 表示「当前对象的某链接指向该集合中的任意一个」。

示例:查「至少有 2 行订单行」的订单头下的所有订单行(即只返回属于这类订单头的行)。

Copy
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」的订单下的订单行。

Copy
[
  { "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) 表示在全局计订单总数。

示例:查每条订单的订单号、日期,并带出订单总条数。

Copy
with total := count(detached Order)

select Order {
    order_no,
    order_date,
    total_count := total
}

结果示意:

Copy
[
  { "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 存审批单号,审批对象 Approvalapproval_notitle。在订单结果中带出审批单标题。

Copy
select Order {
    order_no,
    order_date,
    approval_title := (
        select Approval
        filter .approval_no = Order.approval_no
    ).title
}

结果示意:

Copy
[
  { "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 中将子查询赋值给变量时,子查询要套一层圆括号:

Copy
-- ✅ 正确:子查询用圆括号包裹
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 }

将子查询作为 countsumarray_agg 等函数的参数时,同样需要圆括号——注意此时会出现双层括号(外层是函数调用的括号,内层是子查询的括号),不能省略:

Copy
-- ✅ 正确:双层括号,外层是 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 中独立占一行,都需要圆括号:

Copy
select Order {
    order_no,
    -- ✅ 正确
    latest_line := (
        select .<order[is OrderLine]
        order by .line_no desc
        limit 1
    ) { product_name, qty }
}

记忆口诀:子查询只要不是「顶层语句」,就要套 ()。赋值套、嵌套套、传参套——宁可多套一层,不能少。

在 select 的形状中引用一个已有链接时,有两种写法——直接展开链接用子查询重新 select 链接。它们看似相似,实际语义和能力完全不同。

以订单头的 customer 链接为例:

Copy
-- 写法 A:直接展开链接(shape 语法)
select Order {
    order_no,
    customer: {
        code,
        name_zh := <str>.name['zh-cn']
    }
}
Copy
-- 写法 B:子查询重新 select 链接
select Order {
    order_no,
    customer_info := .customer {
        code,
        name_zh := <str>.name['zh-cn']
    }
}

两者结果完全一样——都是展开 customer 的 code 和中文名。那什么时候必须用写法 B?

直接展开链接(写法 A)只能指定要取哪些字段,不能对链接目标做过滤、排序、限制数量。如果需要这些能力,就必须用子查询形式(写法 B)。

场景:订单行只取前 3 条高价的

Copy
-- ❌ 写法 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 场景下过滤)

Copy
-- 假设 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'] }
}

直接展开 .link: { ... }

子查询 (select .link filter ...) { ... }

语法

链接名: { 字段列表 }

别名 := (select .链接名 filter/order/limit) { 字段列表 }

能否 filter

❌ 不可以

✅ 可以

能否 order by

❌ 不可以

✅ 可以

能否 limit

❌ 不可以

✅ 可以

字段名

与链接同名

可以起新别名(如 top_linesvip_customers

适用场景

简单展开,取全量关联数据

需要对关联数据做筛选、排序、截取

简单记:直接展开 = 「把关联对象的哪些字段带出来」;子查询重取 = 「对关联对象先做一轮 select,再带出字段」。后者多了一层查询能力,但也需要多写子查询和圆括号。

DeepQL 是完全可组合的:任何查询表达式都可以嵌入到另一个查询中,不限制嵌套深度。例如在 select 的计算字段里再写 select + filter,或在 with 里嵌套 with,这些都是合法的。

示例:查每个订单,带出「金额最高的一条订单行」的商品名称。

Copy
select Order {
    order_no,
    top_product := (
        select .<order[is OrderLine] { product_name }
        order by .qty * .price desc
        limit 1
    ).product_name
}

提示:如果反向链接的使用频率很高,可以将其在对象的 schema 中定义为计算链接,后续查询就可以像正向链接一样直接用,不必每次都写反向链接语法。

用法

说明

with 中 select 得到集合,主查询中 filter .链接 in 变量

只保留「关联对象在集合内」的记录

detached

子查询在全局执行,不受当前行/当前 select 范围限制

子查询中 filter .某属性 = 当前对象.某属性

无链接时通过属性关联到另一类对象并取字段

子查询必须加 ()

赋值变量、传入函数、内联计算字段时,子查询都要用圆括号包裹

.link: { ... } vs (select .link filter ...) { ... }

直接展开只能选字段;子查询形式可以 filter / order / limit

任意嵌套

DeepQL 完全可组合,select 内可嵌入 select + filter + order + limit

下一步可以按维度分组并做聚合,见「六、分组与汇总」。

回到顶部

咨询热线

400-821-9199

我们使用 ChatGPT,基于文档中心的内容以及对话上下文回答您的问题。

ctrl+Enter to send