多重子查询性能差因每层可能独立执行、物化临时表、重复扫描,致N×M×K复杂度;含聚合、GROUP BY或LIMIT时优化器常强制走临时表路径,IO与内存开销陡增。

为什么多重子查询常慢得离谱
因为每层子查询都可能触发独立执行、临时表物化、重复扫描——比如三层嵌套里最内层被外层每行调用一次,就变成 N×M×K 复杂度。MySQL 5.7+ 虽会尝试将部分派生表内联,但一旦子查询含聚合、GROUP BY 或 LIMIT,优化器大概率放弃内联,强制走临时表路径,IO 和内存开销陡增。
什么时候该用临时表缓存中间结果
满足以下任一条件时,显式建临时表比依赖优化器更可控:
- 子查询结果集稳定(如每日统计口径不变),且被主查询多次引用
- 子查询本身含 GROUP BY / DISTINCT / 窗口函数,无法被优化器下推或内联
- 子查询返回行数 > 10k,而主查询需 JOIN 或 EXISTS 判断多次
- 你发现
EXPLAIN输出中出现Using temporary; Using filesort且出现在子查询节点
注意:临时表不是万能解药。若中间结果仍需关联大表且无索引,性能可能更差。
怎么建临时表才不踩坑
别直接用 CREATE TEMPORARY TABLE ... SELECT 就完事。关键在三步:建表结构、加索引、控制生命周期。
- 显式定义字段类型和长度,避免
SELECT *导致隐式转换(比如把INT写成BIGINT) - 对后续 JOIN 或 WHERE 中会用到的字段,立刻加索引:
CREATE INDEX idx_user_id ON tmp_orders (user_id) - 临时表只在当前 session 生效,不用手动
DROP;但若脚本复用率高,建议开头加DROP TEMPORARY TABLE IF EXISTS tmp_orders防冲突 - 别在存储过程中反复创建同名临时表——MySQL 不允许同一 session 内重复
CREATE TEMPORARY TABLE
示例:把“每个用户最新订单”这个高频子查询固化
CREATE TEMPORARY TABLE tmp_last_order AS SELECT user_id, MAX(created_at) AS last_time FROM orders GROUP BY user_id; CREATE INDEX idx_user_id ON tmp_last_order (user_id);
临时表之后怎么高效对接主查询
核心原则:让优化器能走索引嵌套循环,而不是回表扫临时表全量。
- JOIN 时确保驱动表小(比如
users表只有几千行),被驱动表(tmp_last_order)有对应索引 - 避免在临时表字段上做函数操作:
WHERE DATE(last_time) = '2026-04-17'会失效索引,改用WHERE last_time >= '2026-04-17' AND last_time - 如果只是做存在性判断(如“是否有近7天订单”),优先用
EXISTS (SELECT 1 FROM tmp_last_order t WHERE t.user_id = u.id AND t.last_time >= ...),比LEFT JOIN更轻量
最容易被忽略的是:临时表的统计信息不会自动更新。如果中间结果集变化剧烈(比如某天订单暴增10倍),优化器仍按旧基数估算执行计划,可能导致选错驱动表。此时可加 ANALYZE TABLE tmp_last_order 强制刷新。

















