SQL中的子查询是指嵌套在其他SQL语句中的查询语句,它可以作为临时结果集、过滤条件或者计算单元存在,是简化复杂计算、实现逻辑模块化的有效手段。通过子查询,我们可以将原本需要多步处理或者逻辑混杂的计算任务,拆分成多个职责单一的独立部分,让整体SQL的逻辑更清晰易懂。

子查询的常见类型
标量子查询
标量子查询返回单个值,通常可以用在SELECT子句、WHERE子句或者HAVING子句中,作为单一的计算结果或者过滤条件。比如我们需要查询所有订单金额高于平均订单金额的用户,就可以用标量子查询先计算平均订单金额。
-- 查询订单金额高于平均订单金额的用户ID和订单金额
SELECT user_id, order_amount
FROM order_table
WHERE order_amount > (
-- 标量子查询:计算平均订单金额
SELECT AVG(order_amount)
FROM order_table
);
列子查询
列子查询返回一列多行的数据,通常用在WHERE子句的IN、ANY、ALL等条件中,作为一组过滤值。比如我们需要查询购买了指定分类商品的用户列表,就可以先通过列子查询获取该分类下的所有商品ID,再匹配订单表中的记录。
-- 查询购买了分类ID为5的商品的所有用户ID
SELECT DISTINCT user_id
FROM order_item_table
WHERE product_id IN (
-- 列子查询:获取分类ID为5的所有商品ID
SELECT product_id
FROM product_table
WHERE category_id = 5
);
表子查询
表子查询返回多行多列的结果集,通常用在FROM子句中,作为临时表参与后续的关联或者计算。比如我们需要统计每个用户的累计订单金额,同时筛选出累计金额超过1000的用户,就可以先通过表子查询计算每个用户的累计金额,再对结果进行过滤。
-- 查询累计订单金额超过1000的用户ID和累计金额
SELECT user_id, total_amount
FROM (
-- 表子查询:计算每个用户的累计订单金额
SELECT user_id, SUM(order_amount) AS total_amount
FROM order_table
GROUP BY user_id
) AS user_total
WHERE total_amount > 1000;
用子查询实现复杂计算的模块化拆解
当遇到需要多步聚合、多表关联、多层条件判断的复杂计算场景时,我们可以通过子查询将整个计算流程拆解为多个独立的逻辑单元,每个单元只完成一个明确的任务,最后再组合这些单元得到最终结果。
场景示例:统计各分类下销量前3的商品
这个需求需要完成三个逻辑步骤:第一步关联商品表和订单明细表计算商品销量,第二步按分类对商品销量排序,第三步筛选出每个分类下排名前3的商品。如果直接写单条SQL会非常冗长,我们用子查询拆分每个步骤:
-- 第一步:子查询计算每件商品的销量,作为临时表t1
WITH product_sales AS (
SELECT
p.category_id,
p.product_id,
p.product_name,
SUM(oi.quantity) AS sales_count
FROM product_table p
JOIN order_item_table oi ON p.product_id = oi.product_id
GROUP BY p.category_id, p.product_id, p.product_name
)
-- 第二步:子查询按分类对商品销量排序,添加排名,作为临时表t2
, ranked_sales AS (
SELECT
category_id,
product_id,
product_name,
sales_count,
-- 按分类分区,销量降序排名
ROW_NUMBER() OVER (PARTITION BY category_id ORDER BY sales_count DESC) AS sales_rank
FROM product_sales
)
-- 第三步:筛选排名前3的记录
SELECT category_id, product_id, product_name, sales_count, sales_rank
FROM ranked_sales
WHERE sales_rank <= 3;
上面的示例使用了公用表表达式(CTE)形式的子查询,每个CTE就是一个独立的逻辑单元,分别负责销量计算、排名计算、结果筛选,整体逻辑清晰,后续如果需要修改某一步的计算逻辑,只需要调整对应的CTE即可,不需要改动整个SQL的结构。
子查询使用的注意事项
- 子查询的性能需要关注,尤其是关联子查询可能会对外部查询的每一行都执行一次,数据量大的时候会有性能问题,可以考虑用
JOIN替代部分关联子查询。 - 子查询的别名在作为临时表使用时必须指定,否则会报语法错误。
- 标量子查询如果返回多行会直接报错,使用时需要确认子查询的结果只会返回单个值。
- 过度嵌套的子查询也会降低可读性,一般建议嵌套层级不要超过3层,层级过多时可以考虑拆分成多个临时表或者CTE。
子查询的核心价值是将复杂的计算逻辑拆解为低耦合的独立单元,在提升SQL可读性的同时,也降低了后续维护的成本,是SQL开发中非常重要的优化思路。