应优先使用 UNION ALL 而非 UNION,因其无去重和排序开销、性能更高;UNION 实际执行 DISTINCT + ORDER BY,导致临时表、文件排序及磁盘 I/O 瓶颈;仅当语义上确需去重且无法在应用层或 CTE 控制时才用 UNION。

UNION 会去重并隐式排序,UNION ALL 不去重、不排序、性能高得多——90% 的场景该用 UNION ALL 却误用了 UNION。
UNION 为什么慢?因为它实际执行了 DISTINCT + ORDER BY
数据库引擎处理 UNION 时,并不是简单合并再删重。它等价于先做 UNION ALL,再套一层 SELECT DISTINCT ... ORDER BY。这意味着:
- 必须把所有子查询结果暂存到临时表(
Using temporary) - 为去重和排序触发
Using filesort,尤其在无索引列上代价极高 - 内存不足时会写磁盘,I/O 成为瓶颈
- 即使你显式加了
ORDER BY,数据库仍可能重复排序(一次为去重,一次为你写的)
UNION ALL 的真实行为:纯拼接,零额外逻辑
UNION ALL 就是字面意思:把各子查询结果按顺序“摞起来”,不做任何比较或调整。所以:
- 不检查行是否重复,不申请额外内存做哈希/排序
- 结果顺序完全由子查询执行顺序决定(比如
SELECT * FROM t1在前,就先出 t1 的数据) - 没有
Using temporary或Using filesort,EXPLAIN 看起来干净利落 - 列数和类型必须匹配,但允许子查询里用别名或表达式,只要最终结构一致
什么时候非用 UNION 不可?别被“去重要求”骗了
业务说“不能有重复”,不等于必须用 UNION。真正需要 UNION 的场景极少,典型包括:
- 合并不同来源的客户表(CRM + 旧系统),且 ID 字段未做全局唯一约束
- 统计口径混用(如“活跃用户”定义在两个子查询中不一致,语义上可能重叠)
- ETL 清洗阶段,明确要 dedupe 后入库
而这些情况往往更适合在应用层或 CTE 中控制去重逻辑,而不是依赖 UNION 的黑盒行为。更常见的是:你用 UNION 只是因为“习惯”或“怕出错”,但实际数据天然不重(比如按时间分表的日志查询、按 ID 范围拆分的用户表)。
一个容易被忽略的坑:ORDER BY 必须放在最后
无论用 UNION 还是 UNION ALL,子查询里加 ORDER BY 是无效的(语法允许但被忽略)。正确写法只有一种:
SELECT id, name FROM users_2023 UNION ALL SELECT id, name FROM users_2024 ORDER BY id;
否则你会得到不可预测的顺序,且 UNION 的隐式排序可能和你的预期冲突——比如你想要按插入时间排,但它按主键排了。
最常被低估的点:UNION 的性能衰减不是线性的。100 万行时它可能比 UNION ALL 慢 11 倍,而这个差距在分页查询(LIMIT OFFSET)或嵌套子查询里会被放大数倍。确认数据无重、不需要排序,就直接换 UNION ALL——别犹豫。


















