UNION ALL 不去重、不排序、不建临时表,仅顺序拼接结果;UNION 强制建临时表、去重并默认排序,执行开销大。优化应先聚焦子查询索引、类型一致性和条件下推,而非盲目替换。

UNION ALL 不做去重,UNION 必须建临时表 + 排序
根本原因在于执行路径差异:UNION 会强制创建临时表、对合并结果做 DISTINCT 去重,并默认按第一列排序;而 UNION ALL 只是把各子查询结果集顺序拼接,不建临时表、不排序、不校验唯一性。
用 EXPLAIN 看执行计划就能验证:只要出现 Using temporary,基本就是 UNION 在干活;UNION ALL 的执行计划里通常看不到这一项。
- 去重需要全量扫描+哈希或排序,数据量越大开销越明显
-
UNION即使子查询本身已有序,最终仍可能触发额外排序 - 如果任意一个子查询含
ORDER BY或LIMIT,UNION还得在临时表上再跑一遍逻辑
列类型不一致时,UNION ALL 仍可能慢,但原因不同
当两个 SELECT 的对应列类型不兼容(比如一边是 VARCHAR(50),另一边是 TEXT),MySQL 会隐式转换——这种转换在 UNION ALL 中同样发生,且可能引发索引失效或全表扫描。但它不会因此变“去重”,只是让单个子查询变慢。
- 检查方式:用
SHOW WARNINGS看是否有Truncated incorrect类提示 - 修复重点不是换
UNION ALL,而是统一列定义或显式CAST - 别指望
UNION ALL能掩盖底层数据类型混乱的问题
INSERT ... SELECT + UNION ALL 的写法容易踩内存坑
如果你写的是 INSERT INTO t1 SELECT ... UNION ALL SELECT ...,MySQL 会先把全部结果加载进内存临时表再插入。一旦总行数超 tmp_table_size 或 max_heap_table_size,就会落盘到磁盘临时表,I/O 暴涨。
- 现象:插入中途变卡、
Created_tmp_disk_tables计数飙升 - 对策:提前调大这两个参数,或拆成多个小批次
INSERT - 更稳的替代:用
CREATE TEMPORARY TABLE分步写入,可控性强
为什么有时候换成 UNION ALL 也没变快?
因为瓶颈根本不在去重,而在子查询本身。比如每个 SELECT 都没走索引、都扫全表、都返回百万行——这时换 UNION ALL 只省了毫秒级去重时间,整体还是慢。
- 先对每个子查询单独
EXPLAIN,确认是否用了索引、是否回表严重 - 避免
SELECT *,只取真正需要的列,减少传输和临时表体积 - WHERE 条件尽量下推:不要写
(SELECT ... UNION ALL SELECT ...) WHERE x=1,而要写成SELECT ... WHERE x=1 UNION ALL SELECT ... WHERE x=1
UNION ALL 只是最后一层“不添乱”的保障。真正拖慢的,往往是那些没加索引的 WHERE、没限制的 JOIN、或者被忽略的字段类型隐式转换。


















