SQL子查询是嵌套在主查询中的查询语句,合理使用能在很多场景下简化查询逻辑、提升执行效率。下面先通过一张示例图了解子查询的基本结构。

SQL子查询的核心应用场景
1 筛选聚合后的结果
当我们需要先对数据进行聚合计算,再根据聚合结果筛选数据时,子查询可以避免先查出全量数据再在应用层过滤的问题。比如要查询订单总金额超过1000的所有用户,就可以先通过子查询计算每个用户的订单总金额,再筛选符合条件的用户。
-- 查询订单总金额超过1000的用户
SELECT user_id, total_amount
FROM (
-- 子查询先计算每个用户的订单总金额
SELECT user_id, SUM(order_amount) AS total_amount
FROM orders
GROUP BY user_id
) AS user_order_summary
WHERE total_amount > 1000;2 关联子查询实现行级过滤
关联子查询会引用主查询中的列,适合实现和每行数据相关的筛选逻辑。比如查询每个部门中工资高于部门平均工资的员工,就可以用关联子查询逐行判断员工工资是否高于所在部门的平均工资。
-- 查询每个部门中工资高于部门平均工资的员工
SELECT e1.emp_id, e1.emp_name, e1.dept_id, e1.salary
FROM employees e1
WHERE e1.salary > (
-- 关联子查询,引用主查询的dept_id计算部门平均工资
SELECT AVG(e2.salary)
FROM employees e2
WHERE e2.dept_id = e1.dept_id
);3 替代多表连接减少数据量
当只需要查询主表数据,同时需要根据从表的条件做筛选时,用子查询替代多表连接可以减少参与计算的数据量,避免连接后产生的冗余数据影响效率。比如查询有未支付订单的用户,用子查询只需要判断是否存在符合条件的订单即可,不需要把用户和订单数据全部连接。
-- 查询有未支付订单的用户
SELECT user_id, user_name
FROM users
WHERE user_id IN (
-- 子查询找出所有有未支付订单的用户ID
SELECT DISTINCT user_id
FROM orders
WHERE order_status = 'unpaid'
);用子查询提升查询效率的注意事项
- 尽量避免在WHERE子句中使用返回大量数据的非关联子查询,这类子查询会多次执行,增加数据库负担。
- 如果子查询的结果集较小,优先使用IN、EXISTS等操作符,IN适合子查询结果集小的情况,EXISTS适合子查询结果集大的情况。
- 给子查询中使用的筛选字段、关联字段建立合适的索引,能大幅提升子查询的执行速度。
- 对于复杂的多层嵌套子查询,可以考虑拆分成临时表或者公共表表达式(CTE),避免嵌套过深导致的性能损耗。
子查询与连接查询的效率对比
下面通过表格对比子查询和连接查询在不同场景下的适用情况:
| 场景 | 子查询优势 | 连接查询优势 |
|---|---|---|
| 只需要判断存在性 | 使用EXISTS子查询,找到第一条匹配就停止扫描,效率高 | 连接会产生冗余数据,效率更低 |
| 需要返回多表字段 | 可能需要多层嵌套,逻辑复杂 | 一次连接就能获取所有需要的字段,逻辑更清晰 |
| 子查询结果集极小 | 执行速度快,资源占用少 | 连接操作的开销相对更高 |
总的来说,SQL子查询是提升查询效率的重要工具,只要结合业务场景合理使用,同时避开常见的性能陷阱,就能让复杂查询的执行效率得到明显提升。