该用临时表:当查询出现Using temporary/filesort且数据量超tmp_table_size,或需多次引用同一中间结果、建索引优化时;应显式定义结构、及时建索引、避免滥用。

临时表该不该用,先看执行计划卡在哪
直接上结论:当查询里出现 Using temporary 或 Using filesort 且数据量超过 tmp_table_size(默认 16MB),说明 MySQL 正在用内部临时表做排序/分组,但这个过程不可控、无索引、不能复用——这时候就得自己建 CREATE TEMPORARY TABLE 把中间结果捞出来。
常见错误现象是:一个带多层 GROUP BY + JOIN 的报表查询跑 20 秒,EXPLAIN 显示 type=ALL 和 rows=1e6,但实际过滤后只剩几千行。这不是数据量问题,是优化器没拿到真实基数。
- 别等
EXPLAIN报Using temporary才动手,只要语句里有两次以上对同一子集的聚合或连接,就该考虑拆 -
SHOW VARIABLES LIKE 'tmp_table_size'和max_heap_table_size必须查,二者取小值才是内存临时表上限 - 如果
SELECT ... INTO OUTFILE或LOAD DATA INFILE能替代,优先用——临时表不是银弹,IO 成本可能更高
CREATE TEMPORARY TABLE 的写法陷阱
MySQL 临时表语法看着简单,但字段定义和引擎选错,性能会断崖下跌。
最常踩的坑是:用 CREATE TEMPORARY TABLE tmp AS SELECT ... 生成表后,发现后续 JOIN 慢得离谱——因为这种写法不带主键、没索引、字段类型可能被隐式降级(比如 VARCHAR(255) 变成 VARCHAR(191))。
- 显式定义结构比
AS SELECT更可控:CREATE TEMPORARY TABLE tmp (id BIGINT PRIMARY KEY, name VARCHAR(100), INDEX idx_name (name)) ENGINE=MEMORY - 数据量预估超 50 万行?别硬撑
ENGINE=MEMORY,直接加ENGINE=InnoDB,否则触发磁盘落盘反而更慢 - 字段含中文或特殊字符?
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci必须显式声明,否则跨会话或 JOIN 时乱码+隐式转换 - 别用
SELECT * INTO—— MySQL 不支持这个语法,会报ERROR 1327 (42000): Undeclared variable
临时表怎么用才真正提速
建完表只是开始,真正起效靠三件事:索引、复用、隔离。
比如要查“近 30 天下单用户中,复购率 > 2 的城市 TOP 10”,原始写法嵌套 4 层子查询,EXPLAIN 显示扫描 200 万行;改用临时表后,把“近 30 天下单用户”先存进 temp_users,再在这个表上建 INDEX idx_city (city),后续统计直接走索引,耗时从 18s 降到 1.2s。
- 建完表立刻加索引:
CREATE INDEX idx_user_id ON temp_orders (user_id),别等SELECT时才发现没走索引 - 同一个会话里,临时表可被多次
INSERT、UPDATE、JOIN,但别在EXECUTE IMMEDIATE或PREPARE动态 SQL 里引用——会报Table 'temp_xxx' doesn't exist - 存储过程中用临时表,记得在
END前手动DROP TEMPORARY TABLE IF EXISTS tmp,避免调试时重复创建报ERROR 1050 (42S01) - 并发场景下,不同连接可以同名建表,但别依赖这个特性做“伪全局缓存”——
tmp_table_size是会话级的,A 连接建的temp_dataB 连接根本看不见
临时表和 CTE、派生表到底选谁
不是所有中间结果都该用临时表。CTE(WITH)和派生表(子查询)在简单场景下更轻量,但一旦涉及多次引用、大结果集或需要索引,临时表就是唯一解。
典型误判是:看到 CTE 写起来清爽,就把 50 万行聚合结果塞进 WITH sales AS (SELECT ...),然后在主查询里反复引用 sales —— MySQL 会为每次引用重跑一遍 CTE,等于算 3 次。
- CTE 适合逻辑拆分、可读性优先,且结果集 ≤ 1 万行
- 派生表适合单次使用、无需索引的中间集
- 临时表适合:结果要被 ≥ 2 次引用、需
ORDER BY/LIMIT后再JOIN、或要人工干预执行计划(比如强制走哈希连接) - 函数里不能用临时表,只能用派生表或 CTE;视图里也不能建临时表,会直接报
ERROR 1356 (HY000)
临时表真正的复杂点不在语法,而在对执行路径的预判——你得清楚知道哪一步在拖慢查询,以及那一步的结果是否值得固化。很多人建了表却忘了加索引,或者在小数据量上硬套方案,反而引入额外 IO 开销。


















