在 SQL 优化中,一般遵循**“非必要不使用子查询”**的原则,因为子查询可能会带来额外的计算开销,影响查询效率。但是,并不是所有子查询都需要避免,有时子查询是最优解,具体要根据实际场景选择合适的优化方式。
1、为什么尽量避免子查询?
-
子查询可能执行多次
-
非相关子查询(Non-correlated subquery) 执行一次
-
相关子查询(Correlated subquery) 可能要对每一行都执行一次,严重影响性能
-
-
索引可能无法生效
-
子查询的结果通常存储在临时表中,可能导致索引失效
-
-
可能引入不必要的计算
-
许多子查询可以通过
JOIN
或EXISTS
替代,避免重复计
-
2、如何优化?
(1)使用子查询
例:
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
这条SQL中,子查询会遍历 orders
表的所有数据,然后再匹配 users
表,可能导致性能下降。
优化(使用 JOIN
):
SELECT DISTINCT users.*
FROM users
JOIN orders ON users.id = orders.user_id;
避免了子查询的重复执行,直接连接两个表,提高查询效率。
(2)使用 IN
子查询
例:
SELECT * FROM products
WHERE category_id IN (SELECT id FROM categories WHERE name = 'Electronics');
子查询会先获取 id
列,再在 products
表中查找,可能导致索引失效。
优化(使用 JOIN
):
SELECT p.* FROM products p
JOIN categories c ON p.category_id = c.id
WHERE c.name = 'Electronics';
JOIN 让查询引擎直接连接表,而不是单独计算子查询,提高查询速度。
(3)使用 NOT IN
例:
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM orders);
NOT IN
可能导致索引失效,并且当 orders.user_id
里有 NULL
值时,查询结果可能不正确。
优化(NOT EXISTS
):
SELECT * FROM users u
WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
-
NOT EXISTS
通常比NOT IN
更高效,因为它在找到匹配的记录时就会停止搜索,而NOT IN
需要计算整个子查询结果。 -
避免
NULL
值带来的问题
3、什么时候可以使用子查询?
虽然子查询通常会带来性能问题,但在某些情况下是合理的。
(1)子查询返回的结果是一个固定值(如 MAX()
、MIN()
)
(2)子查询无法用 JOIN
代替(如分组统计场景)
(3)业务逻辑复杂,避免 JOIN
使 SQL 变得过于复杂。
例如:
SELECT name, price FROM products
WHERE price = (SELECT MAX(price) FROM products WHERE category_id = products.category_id);
这种情况下,子查询返回的是单个值,对性能影响较小。
4、总结
(1)可以优化子查询的场景:
-
IN
子查询 →JOIN
-
NOT IN
子查询 →NOT EXISTS
-
相关子查询 →
JOIN
结合GROUP BY
(2)适合使用子查询的情况
-
需要计算单个聚合值(如
MAX()
、SUM()
) -
业务逻辑导致
JOIN
过于复杂
以上就是关于子查询的相关使用策略!