临时表提速十倍仅发生在子查询重复计算且原计划反复扫描大表或生成无索引中间结果时;关键在于显式建索引、精简字段、匹配引擎,并避免并发覆盖与无效物化。

临时表不是“一建就快”,它能快十倍,只发生在子查询被重复计算、且原始执行计划反复扫描大表或生成无索引中间结果的场景下——比如同一个聚合结果在 WHERE 和 JOIN 中各用一次,而原写法让 MySQL 执行了两次全量 GROUP BY。
为什么子查询会被反复执行
MySQL 对出现在多个位置的非相关子查询(如 FROM 中的派生表、WHERE 中的 IN 子句),并不保证物化一次复用。尤其在 MySQL 5.7 及更早版本中,SELECT * FROM t1 WHERE id IN (SELECT id FROM t2 WHERE ...) 和 JOIN (SELECT ...) AS tmp 在执行计划里可能都展开为多次独立扫描。
- EXPLAIN 输出里若出现多次
DEPENDENT SUBQUERY或SUBQUERY(非 MATERIALIZED),说明没复用 - MySQL 8.0+ 的 CTE 仅在部分条件下被自动物化,不可依赖;
WITH ... AS不等于临时表 - 子查询若含
NOW()、RAND()、用户变量等,数据库会拒绝物化,强制每次重算
临时表快的关键:索引 + 单次计算
临时表真正起效,靠的是把“不可控的中间结果”变成“可控的物理结构”:你决定存哪些字段、用什么引擎、加什么索引。
- 必须显式建索引:
CREATE INDEX ix_tmp_uid ON tmp_user_stats(user_id),否则后续JOIN或WHERE仍是全表扫描 - 字段要精简:只保留
user_id、order_count,别用SELECT *,减少写入和内存占用 - 引擎需匹配数据特征:含
TEXT/BLOB或行数超tmp_table_size时,MEMORY 引擎会静默降级为 MyISAM,此时应显式指定ENGINE=InnoDB
哪些子查询改临时表反而更慢
不是所有嵌套都适合拆。盲目替换可能引入额外 I/O、锁等待和会话资源开销。
- 子查询只被引用 1 次,且结果集小( 直接计算
- 子查询含滑动时间窗口,如
WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR):缓存失效快,临时表无法复用 - 主表关联键未去重,又用
LEFT JOIN tmp ON ...:可能因一对多引发笛卡尔积,查出错误行数 - 事务中创建后未在同事务内完成所有操作:虽临时表生命周期是会话级,但跨事务引用需重新建
最容易被忽略的一点:临时表名没加会话标识。在连接池复用场景下,tmp_orders 可能被并发请求相互覆盖或阻塞,建议用 tmp_orders_20261103_0408 这类带时间戳的命名方式。索引不是可选项,是必选项——没索引的临时表,大概率比原生子查询还慢。










