临时表+JOIN比WHERE IN更快,因其将O(n)解析压为O(1),但前提是临时表和目标表关联字段均有索引;否则易退化为笛卡尔积或全表扫描。

临时表 + JOIN 关联更新不是“快一点”,而是把原本 O(n) 的解析开销压成 O(1),前提是临时表和目标表的关联字段都有索引。没索引或用错 JOIN 类型,性能反而比 WHERE IN 更差。
临时表必须显式建主键或唯一索引
MySQL 和 PostgreSQL 都不会自动给临时表加索引,CREATE TEMPORARY TABLE tmp (id INT, val TEXT) 这种写法创建的表在 JOIN 时大概率触发笛卡尔积——因为优化器找不到高效驱动路径。
- 正确写法是:
CREATE TEMPORARY TABLE tmp (id BIGINT PRIMARY KEY, val TEXT)(MySQL)或CREATE TEMP TABLE tmp (id BIGINT PRIMARY KEY, val TEXT)(PostgreSQL) - SQL Server 要写成:
CREATE TABLE #tmp (id BIGINT PRIMARY KEY, val NVARCHAR(50)) - 千万别用
CREATE TEMPORARY TABLE AS SELECT ...,它不支持PRIMARY KEY语法糖,字段类型也可能被隐式转成VARBINARY,导致后续JOIN失败
UPDATE 必须用 INNER JOIN,不能用 LEFT JOIN
LEFT JOIN 在批量更新里是危险操作:没匹配上的行会把目标字段设为 NULL,而不是跳过——这和业务预期完全相反。
- MySQL/PostgreSQL 正确写法:
UPDATE t1 INNER JOIN tmp ON t1.id = tmp.id SET t1.status = tmp.status - SQL Server 写法不同:
UPDATE t1 SET t1.status = t2.status FROM users t1 INNER JOIN #tmp t2 ON t1.id = t2.id - PostgreSQL 必须用
FROM语法:UPDATE users SET status = t.status FROM tmp_update_data t WHERE users.id = t.id - 所有数据库都要求
WHERE或ON条件里的字段有索引,否则执行计划会显示type=ALL,rows接近全表数
分批写入临时表防超限和OOM
一次性往临时表塞 50 万行,大概率触发 max_allowed_packet 报错或客户端内存溢出。临时表数据不是“越快写完越好”,而是要控节奏。
- 每批 INSERT 控制在 1000–5000 行,例如:
INSERT INTO tmp VALUES (1,'a'),(2,'b'),...,(5000,'z') - PostgreSQL 更推荐用
COPY tmp FROM STDIN流式导入,比多值 INSERT 快 5–10 倍 - MySQL 可用
LOAD DATA INFILE(需文件权限)或客户端分批提交 - 别依赖
ROW_COUNT()判断是否还有下一批——当最后一批不足 5000 行时,它仍返回实际插入数,不是 0
临时表生命周期和事务不是一回事
这是最容易卡住的地方:临时表绑定的是会话(session),不是事务(transaction)。你在事务里 CREATE TEMPORARY TABLE,然后 ROLLBACK,表依然存在;但连接一断,它就自动销毁,不用 DROP。
- 如果要跑多批次更新,每批都该重建临时表(
DROP+CREATE),而不是TRUNCATE后重用——残留数据可能干扰下一批逻辑 - 临时表无法跨连接共享,应用层多个线程不能共用同一个
#tmp或tmp - 在存储过程中建临时表没问题,但别指望它在调用结束后还留着供下次使用
真正决定速度的从来不是 SQL 写得多短,而是你有没有让优化器看清“只查这几行”。临时表只是载体,索引才是引擎——漏掉任何一个主键或关联字段索引,整条链路就退化成全表扫描。

















