答案是UNION或子查询会触发临时表,因MySQL必须物化DERIVED子查询结果、合并UNION结果集或处理DEPENDENT SUBQUERY中的聚合操作,EXPLAIN中select_type为DERIVED/UNION/UNION RESULT或Extra含Using temporary即为明确信号。
union 或子查询触发了临时表
Navicat 里点「解释」看到 select_type 是 DERIVED、UNION 或 UNION RESULT,基本就说明 MySQL 内部建了临时表。这不是 Navicat 的问题,而是 MySQL 执行器在解析 SQL 时的必然行为。
-
DERIVED出现在FROM子句的子查询中:MySQL 必须先把子查询结果物化成一个临时表,再拿它去 JOIN 或过滤 -
UNION(第二个及之后的 SELECT)和UNION RESULT总是伴随临时表:哪怕你写的是UNION ALL,MySQL 仍需用临时表承载合并后的结果集(只是不排序、不去重) -
DEPENDENT SUBQUERY虽不直接标“临时表”,但若外层每行都触发一次子查询执行,且子查询本身含 GROUP BY / DISTINCT / ORDER BY,也可能隐式生成临时表
ORDER BY / GROUP BY 没走索引,强制使用临时表
即使 SQL 看起来很“平”,比如单表 SELECT * FROM user ORDER BY created_at DESC LIMIT 20,只要 created_at 上没索引,或者索引不能覆盖排序需求(例如复合索引顺序不匹配),MySQL 就会回表 + 文件排序,过程中可能申请内存临时表(Using filesort),撑大后落地磁盘(Using temporary)。
- 检查
Extra列是否出现Using temporary—— 这是明确信号 -
tmp_table_size和max_heap_table_size设置过小,会让本可在内存完成的临时表提前落盘,显著拖慢速度 -
GROUP BY若无法利用索引做松散索引扫描(Loose Index Scan),也会走临时表 + 排序
JOIN 时驱动表选错或缺少关联字段索引
多表 JOIN 中,如果被驱动表(即非第一张表)的 ON 条件字段没索引,MySQL 可能放弃块嵌套循环(BNL),改用老式嵌套循环(NLJ),并为中间结果建临时表缓存;更常见的是,当 STRAIGHT_JOIN 被忽略、优化器误判行数,导致大表作驱动表,小表被反复扫描——这时 Extra 里常带 Using join buffer (Block Nested Loop),本质也是临时空间管理。
- 用
EXPLAIN FORMAT=JSON查看join_buffer_size实际使用量,比看文字描述更准 -
JOIN字段类型不一致(如VARCHARvsCHAR,或字符集不同),会导致索引失效,间接诱发临时表 - 显式加
STRAIGHT_JOIN强制连接顺序,有时能绕过优化器误判,避免临时表生成
临时表本身不是 bug,但线上高并发场景下,频繁创建/销毁临时表会争抢内存、触发磁盘 I/O,甚至耗尽 tmpdir 空间。真正要盯的不是“为什么有”,而是“能不能去掉”——重点看 select_type 和 Extra 两列组合,它们才是临时表生成路径的直接证据。


















