UNION比UNION ALL更消耗性能,根本原因在于强制执行去重(DISTINCT)和隐式排序:需将所有结果写入临时表、全列排序(Using filesort)、遍历去重;而UNION ALL直接流式拼接,无临时表、无排序、无比较,100万行实测慢11倍以上。

UNION比UNION ALL更消耗性能,根本原因在于它多做了两件事:去重(DISTINCT)和隐式排序。这两步不是可选优化,而是强制执行的底层行为。
去重需要全量比较和临时存储
UNION必须确保最终结果中没有重复行,而判断“重复”需逐行比对所有列值。数据库引擎实际执行逻辑等价于:
- 先把所有子查询结果拼成一个大结果集(即先做UNION ALL)
- 把整个结果集写入临时表(内存或磁盘)
- 对临时表所有列进行全字段排序(Using filesort)
- 遍历排序后结果,只保留相邻不重复的行
一旦数据量超出内存容量,临时表就会落盘,I/O延迟直接拉高耗时。实测显示:100万行时,UNION常触发磁盘临时表,而UNION ALL全程在内存流式输出。
隐式排序带来额外开销
多数数据库(如MySQL、SQL Server)在UNION过程中默认按所有列升序排序,只为方便去重。这个排序:
- 与你是否写
ORDER BY无关——即使没写,它也发生 - 可能破坏原查询的索引有序性,导致后续过滤或连接失效
- 时间复杂度为O(n log n),随数据量非线性增长
而UNION ALL完全跳过这一步,各子查询结果按执行顺序直接追加,零排序、零哈希、零比较。
执行计划能清楚看到差异
用EXPLAIN查看执行计划时,UNION通常带两个关键标记:
- Using temporary:说明创建了临时表
- Using filesort:说明触发了外部排序
UNION ALL则不会出现这两项。100万行测试中,UNION耗时9200ms,UNION ALL仅810ms——慢了11倍以上,瓶颈就在这些标记背后的操作。
业务上是否真需要这个“保险”?
UNION的代价换来的是语义安全:只要两行完全相同,就只留一行。但很多场景其实天然无重复:
- 按年/月分表的日志(
log_2023+log_2024) - ID全局唯一的分库用户表(
user_shard1+user_shard2) - CTE中构造的不同维度中间结果
若业务已保证无重复,还用UNION,等于主动给数据库加三重负担:临时表、排序、磁盘I/O。


















