减少子查询的关键是用JOIN、派生表、EXISTS或临时表替代嵌套逻辑:①用INNER/LEFT JOIN替代相关子查询;②用CROSS JOIN派生表替代非相关聚合子查询;③用EXISTS替代大表IN子查询并加索引;④复杂场景拆分至临时表缓存中间结果。

减少子查询的关键是把嵌套逻辑“拉平”,用连接(JOIN)或临时表替代,让MySQL执行计划更高效、更可控。
用JOIN代替相关子查询
相关子查询(即子查询依赖外层表字段)每行执行一次,性能极差。多数场景可改写为LEFT JOIN或INNER JOIN。
- 原写法:SELECT id, name FROM users WHERE id IN (SELECT user_id FROM orders WHERE status = 'paid')
- 优化后:SELECT DISTINCT u.id, u.name FROM users u INNER JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid'
- 注意:用DISTINCT防重复;若需保留无订单用户,改用LEFT JOIN + WHERE o.user_id IS NOT NULL
用派生表(Derived Table)替代非相关子查询
不依赖外层的子查询(如聚合结果),可提前算好作为临时结果集,避免多次计算。
- 原写法:SELECT name, (SELECT AVG(score) FROM exams) AS avg_score FROM students
- 优化后:SELECT s.name, dt.avg_score FROM students s CROSS JOIN (SELECT AVG(score) AS avg_score FROM exams) dt
- CROSS JOIN更语义清晰;也可用JOIN(无ON条件)或直接在SELECT中用变量(需确保单行结果)
用EXISTS替代IN(尤其大表子查询)
当子查询返回大量ID,且只需判断存在性时,EXISTS通常比IN更快,因为它能短路退出。
- 原写法:SELECT * FROM products WHERE category_id IN (SELECT id FROM categories WHERE active = 1)
- 优化后:SELECT * FROM products p WHERE EXISTS (SELECT 1 FROM categories c WHERE c.id = p.category_id AND c.active = 1)
- 确保子查询中关联字段有索引(如categories.id、products.category_id)
必要时拆分+临时表缓存中间结果
复杂多层嵌套子查询,尤其是含GROUP BY、窗口函数或重复计算的,可先存入临时表,再JOIN使用。
- 例如统计每个用户最近3笔订单总金额,可先建临时表:CREATE TEMPORARY TABLE tmp_last3 AS SELECT user_id, SUM(amount) sum_amt FROM (SELECT user_id, amount, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) rn FROM orders) t WHERE rn
- 再与users表JOIN:SELECT u.name, t.sum_amt FROM users u LEFT JOIN tmp_last3 t ON u.id = t.user_id
- 临时表自动索引主键,支持WHERE/JOIN加速,也便于EXPLAIN分析










