tencent cloud

TDSQL Boundless

子查询

PDF
聚焦模式
字号
最后更新时间: 2026-03-26 14:56:38
本文档介绍如何在 TDSQL Boundless 数据库中使用子查询。子查询是嵌套在另一个查询内部的 SQL 查询,允许你在一条语句中使用另一个查询的结果。

子查询的分类

在 TDSQL Boundless 中,子查询通常有以下几种形式:

标量子查询

标量子查询返回单行单列的值,可以出现在 SELECT 列表、WHERE 条件等任何需要单个值的位置。其关键特征是子查询的结果等价于一个常量值。
SELECT
c_name,
c_acctbal,
(SELECT AVG(c_acctbal) FROM customer) AS avg_balance
FROM customer
LIMIT 5;

派生表

派生表是放在 FROM 子句中的子查询,作为一个临时表参与后续查询。其关键特征是子查询必须用括号包裹并指定别名。
SELECT seg.c_mktsegment, seg.cnt
FROM (
SELECT c_mktsegment, COUNT(*) AS cnt
FROM customer
GROUP BY c_mktsegment
) seg
ORDER BY seg.cnt DESC;

存在性测试

通过 EXISTSNOT EXISTSINNOT IN 等关键字判断子查询是否返回数据,结果是布尔值。其关键特征是不关心子查询返回的具体值,只关心是否有行存在。
-- EXISTS:判断是否存在匹配行
SELECT c_name FROM customer c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.o_custkey = c.c_custkey
);

-- IN:判断值是否在结果集中
SELECT c_name FROM customer
WHERE c_nationkey IN (SELECT n_nationkey FROM nation WHERE n_name = 'CHINA');

集合比较

使用 ANYALLSOME 关键字将一个值与子查询返回的结果集进行比较。其关键特征是比较运算符(=>< 等)与 ANY/ALL 组合使用。
-- = ANY 等价于 IN
SELECT c_name, c_acctbal FROM customer
WHERE c_acctbal > ANY (
SELECT o_totalprice FROM orders WHERE o_orderstatus = 'F'
);

-- > ALL:大于子查询返回的所有值
SELECT c_name, c_acctbal FROM customer
WHERE c_acctbal > ALL (
SELECT AVG(c_acctbal) FROM customer GROUP BY c_mktsegment
);

作为比较运算符操作数

子查询直接作为比较运算符(><=>=<=<>)的一侧操作数。其关键特征是子查询必须返回单行单列(即标量),与标量子查询的区别在于它出现在 WHERE/HAVING 的比较条件中。
SELECT o_orderkey, o_totalprice
FROM orders
WHERE o_totalprice > (
SELECT AVG(o_totalprice) FROM orders
);

子查询的相关性

根据子查询是否引用了外层查询的列,可分为非关联子查询关联子查询两类。

非关联子查询

非关联子查询不引用外层查询的任何列,其结果独立于外层查询。TDSQL Boundless 会先执行内层子查询,将结果作为常量代入外层查询。
查询账户余额高于所有客户平均余额的客户:
SELECT c_custkey, c_name, c_acctbal
FROM customer
WHERE c_acctbal > (
SELECT AVG(c_acctbal) FROM customer
);
TDSQL Boundless 在处理该查询时,会先执行内层子查询:
SELECT AVG(c_acctbal) FROM customer;
假设计算结果为 4990.51,则外层查询等价于:
SELECT c_custkey, c_name, c_acctbal
FROM customer
WHERE c_acctbal > 4990.51;
使用 IN 子查询查找有过订单的客户:
SELECT c_custkey, c_name, c_mktsegment
FROM customer
WHERE c_custkey IN (
SELECT DISTINCT o_custkey FROM orders
WHERE o_orderdate >= '1995-01-01'
);
内层子查询独立执行,返回一组 o_custkey 值,外层查询在这组值中进行匹配。

关联子查询

关联子查询引用了外层查询的列,因此内层查询的结果依赖于外层查询当前正在处理的行。从逻辑上看,关联子查询需要对外层的每一行都重新执行一次内层查询。
查询每个客户中金额最大的订单:
SELECT o_orderkey, o_custkey, o_totalprice, o_orderdate
FROM orders o1
WHERE o_totalprice = (
SELECT MAX(o2.o_totalprice)
FROM orders o2
WHERE o2.o_custkey = o1.o_custkey
);
内层子查询引用了外层的 o1.o_custkey,对于外层每一行,子查询计算该客户的最大订单金额,然后只保留金额等于最大值的行。
查询账户余额高于同市场细分客户平均余额的客户:
SELECT c1.c_custkey, c1.c_name, c1.c_mktsegment, c1.c_acctbal
FROM customer c1
WHERE c1.c_acctbal > (
SELECT AVG(c2.c_acctbal)
FROM customer c2
WHERE c2.c_mktsegment = c1.c_mktsegment
);
TDSQL Boundless 优化器会尝试对关联子查询进行去关联(Unnesting)优化,将其改写为等价的 JOIN 查询以提升性能。例如上述查询可能被改写为:
SELECT c1.c_custkey, c1.c_name, c1.c_mktsegment, c1.c_acctbal
FROM customer c1
INNER JOIN (
SELECT c_mktsegment, AVG(c_acctbal) AS avg_acctbal
FROM customer
GROUP BY c_mktsegment
) c2 ON c1.c_mktsegment = c2.c_mktsegment
WHERE c1.c_acctbal > c2.avg_acctbal;
改写后的查询只需对 customer 表扫描两次(一次聚合、一次连接),而不是对每个客户都执行一次子查询,性能显著提升。

常见子查询场景

EXISTS 子查询

EXISTS 用于判断子查询是否返回了至少一行数据,常用于存在性检查。
查询有过高金额订单(金额 > 300000)的客户:
SELECT c.c_custkey, c.c_name, c.c_acctbal
FROM customer c
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.o_custkey = c.c_custkey
AND o.o_totalprice > 300000
);
查询没有下过订单的客户(NOT EXISTS):
SELECT c.c_custkey, c.c_name, c.c_phone
FROM customer c
WHERE NOT EXISTS (
SELECT 1 FROM orders o
WHERE o.o_custkey = c.c_custkey
);
NOT EXISTS 在语义上等价于 LEFT JOIN ... WHERE ... IS NULL,但在某些场景下两者的执行效率不同,可通过 EXPLAIN 进行比较。

IN 子查询

IN 用于判断某个值是否在子查询返回的结果集中。
查询来自 ASIA 区域国家的客户:
SELECT c_custkey, c_name, c_nationkey
FROM customer
WHERE c_nationkey IN (
SELECT n_nationkey FROM nation
WHERE n_regionkey IN (
SELECT r_regionkey FROM region
WHERE r_name = 'ASIA'
)
);
IN 与 EXISTS 的选择:当子查询返回的结果集较小时,INEXISTS 性能差异不大;当外层表较小而子查询结果集较大时,EXISTS 通常更高效。

标量子查询

标量子查询返回单个值,可以出现在 SELECT 列表、WHERE 条件等位置。
在 SELECT 列表中使用标量子查询 — 查询每个订单及其客户名称:
SELECT
o_orderkey,
o_totalprice,
o_orderdate,
(SELECT c_name FROM customer WHERE c_custkey = o_custkey) AS customer_name
FROM orders
WHERE o_orderdate = '1995-03-15';
SELECT 列表中的标量子查询在逻辑上会对每一行执行一次,当外层行数较多时建议改写为 JOIN:
SELECT
o.o_orderkey,
o.o_totalprice,
o.o_orderdate,
c.c_name AS customer_name
FROM orders o
INNER JOIN customer c ON o.o_custkey = c.c_custkey
WHERE o.o_orderdate = '1995-03-15';

派生表(FROM 子句中的子查询)

子查询放在 FROM 子句中作为临时表(派生表)使用。
查询每个市场细分中账户余额最高的客户:
SELECT c.c_custkey, c.c_name, c.c_mktsegment, c.c_acctbal
FROM customer c
INNER JOIN (
SELECT c_mktsegment, MAX(c_acctbal) AS max_acctbal
FROM customer
GROUP BY c_mktsegment
) seg_max ON c.c_mktsegment = seg_max.c_mktsegment
AND c.c_acctbal = seg_max.max_acctbal;
统计各订单优先级的订单数量和平均金额,只保留数量超过 10000 的优先级:
SELECT *
FROM (
SELECT
o_orderpriority,
COUNT(*) AS order_count,
AVG(o_totalprice) AS avg_amount
FROM orders
GROUP BY o_orderpriority
) summary
WHERE order_count > 10000
ORDER BY order_count DESC;


帮助和支持

本页内容是否解决了您的问题?

填写满意度调查问卷,共创更好文档体验。

文档反馈