临时表仅在子查询结果稳定、被多次引用且可加索引时才比子查询快;需显式建索引,避免DEPENDENT SUBQUERY,不可盲目替代未优化的单次扫描。

临时表不是“一建就快”,它只在特定条件下比子查询快——关键看子查询是否被重复引用、能否预计算、以及有没有索引支撑。
什么时候该用临时表,而不是直接写子查询
临时表真正起作用的场景很具体:子查询结果要被多次 JOIN 或 WHERE 过滤,且本身不随外层行变化(即无相关性)。比如统计每个客户最近3个月订单总额,再用这个结果去关联客户信息、筛选高价值客户——这个聚合结果是稳定的,适合落地。
- ✅ 适合:
SELECT customer_id, SUM(amount) FROM orders WHERE order_date > DATE_SUB(NOW(), INTERVAL 3 MONTH) GROUP BY customer_id→ 落地为临时表后可加索引,后续 JOIN 快 - ❌ 不适合:
SELECT * FROM logs WHERE created_at > DATE_SUB(NOW(), INTERVAL 1 HOUR)→ 时间窗口滑动,缓存秒级失效,建表+插入反而拖慢 - ⚠️ 小心:
WHERE id IN (SELECT id FROM t WHERE status = ?)→ 如果status是参数,且值变化频繁,临时表无法复用,不如改用EXISTS或联合索引
MySQL 临时表必须加索引,否则等于白建
临时表默认没索引,而后续 JOIN 或 WHERE 条件若依赖某列(如 customer_id),不建索引就会触发全表扫描,性能可能比原生子查询还差。MySQL 的 CREATE TEMPORARY TABLE 不会自动推导主键或索引,必须手动补。
- 建表后立刻执行:
CREATE INDEX ix_tmp_cid ON tmp_orders(customer_id) - 如果要按时间范围再过滤,考虑联合索引:
CREATE INDEX ix_tmp_cid_dt ON tmp_orders(customer_id, order_date) - 避免用
SELECT * INTO #tmp这类隐式方式(SQL Server 风格),MySQL 不支持;必须显式CREATE TEMPORARY TABLE ... AS SELECT ...
别把 CTE 当临时表用,尤其在 MySQL 5.7 和 SQL Server 中
WITH 子句在 MySQL 5.7 和大多数 SQL Server 版本里只是语法糖,执行计划中仍会展开为多次子查询,不会物化。只有 MySQL 8.0+ 的 WITH 在部分场景下可能被优化器自动物化,但不可控;PostgreSQL 12+ 需显式写 MATERIALIZED 才生效。
- 想确认是否真物化?看
EXPLAIN FORMAT=TREE(MySQL 8.0+)或EXPLAIN输出里有没有MATERIALIZED标记 - 如果看到
DEPENDENT SUBQUERY依然高频出现,说明 CTE 没起作用,得换临时表或重写为 JOIN - 临时表名建议带会话标识,如
tmp_orders_20261014_1556,避免并发时同名冲突(尤其连接池复用场景)
真正卡住性能的,往往不是“要不要缓存”,而是“内层有没有走索引”。先跑一遍 EXPLAIN,看子查询的 type 是不是 ALL 或 index、rows 是否爆炸——这些信号比急着建临时表重要得多。临时表解决的是“重复计算”问题,不是“单次扫描慢”问题。










