UNION 比 UNION ALL 慢的根本原因是强制排序+全局去重+临时表三重开销,时间复杂度为 O(n log n),而 UNION ALL 是纯流式 O(n);100 万行时性能差距超 11 倍。

UNION 比 UNION ALL 慢,根本原因不是“多做了去重”,而是它强制触发了排序 + 全局去重 + 临时表三重开销,哪怕你只查两列、数据天然不重复。
UNION 实际执行的是 DISTINCT + ORDER BY
数据库不会先拼再删重;它等价于把所有子查询结果先用 UNION ALL 拼进一个临时结构,再套一层 SELECT DISTINCT ... ORDER BY。这意味着:
-
EXPLAIN中必现Using temporary和Using filesort,哪怕你没写ORDER BY - 所有字段都要参与排序和比对——加一个
TEXT列,内存占用可能翻倍 - NULL 值会被统一视为相同,但隐式类型转换失败(如
VARCHARvsTEXT)时,两者都报错,别指望UNION ALL能绕过
性能差距随数据量非线性放大
这不是常数倍慢,而是算法复杂度跳变:UNION 是 O(n log n) 排序 + O(n) 去重扫描,UNION ALL 是纯流式 O(n) 输出。
- 10 万行:UNION ALL 约 82ms,UNION 约 620ms(7.5 倍)
- 100 万行:UNION ALL 约 810ms,UNION 约 9200ms(11 倍以上)
- 瓶颈不在 CPU,而在临时表写 SSD、内存不足时换出到磁盘、后续 JOIN 因排序丢失索引顺序
嵌套子查询里用 UNION 更危险
当 UNION 出现在 WHERE id IN (SELECT ... UNION SELECT ...) 这类子查询中,优化器常放弃索引,直接全表扫描主表。
- 常见症状:
SHOW PROCESSLIST卡在Copying to tmp table,磁盘 IO 暴涨 - 更稳做法:用
CREATE TEMPORARY TABLE或 CTE 预存中间结果,再用JOIN - 如果各分支 WHERE 条件没走索引(比如
LIKE '%张%'),整个 UNION 就被拖垮,UNION ALL同样逃不掉
什么时候真该用 UNION?
只看一件事:业务是否允许同一行(所有列值完全一致)出现多次。不是“看起来像重复”,而是整行逐字段比对后必须唯一。
- 必须用:跨系统拉客户数据(CRM + 旧库),ID 可能撞车
- 可换
UNION ALL:按年分表的日志(log_2023+log_2024)、分库用户表(ID 全局唯一) - 最容易忽略的坑:两个子查询结果里都有
NULL,UNION 会合并成一行,UNION ALL保留全部——这不算 bug,是标准行为
别默认选 UNION 图省事;先确认业务能否接受重复,再决定要不要扛那多出来的排序、临时表和磁盘 IO。

















