union会自动去重并排序,union all仅拼接结果保留重复行;前者因distinct操作引发排序/哈希及可能的磁盘io,性能显著低于后者,执行计划中union必含distinct节点而union all为纯append流式输出。

UNION 会触发去重,UNION ALL 不会
这是最核心的差异。UNION 在合并多个 SELECT 结果集后,会自动执行等效于 DISTINCT 的操作,把完全相同的行只保留一条;UNION ALL 则只是把结果逐行拼接,不管有没有重复。
常见错误现象:用 UNION 查两个日志表,发现耗时 1.8 秒且 CPU 占用飙升;换成 UNION ALL 后降到 210ms —— 这不是偶然,是去重逻辑在后台做了排序或哈希计算,还可能写磁盘临时文件。
注意:NULL 值在 UNION 中被视为相等,比如两行都是 (1, NULL),会被当成重复行删掉;UNION ALL 会原样保留。
列数、类型、顺序必须严格一致
两个操作符对结构兼容性要求完全相同,不满足就会报错:ERROR 1222 (21000): The used SELECT statements have a different number of columns 或类型冲突。
实操建议:
- 别依赖隐式转换——比如
INT和VARCHAR列直接UNION可能成功,但行为不稳定,MySQL 8.0+ 会更严格 - 显式用
CAST或COALESCE对齐类型,例如把可能为 NULL 的id写成COALESCE(id, 0) - 列名以第一个
SELECT为准,后续子句的别名无效,别指望靠第二个子句改字段名
UNION ALL 性能优势不是“快一点”,而是“少一层执行阶段”
看执行计划就能确认:UNION 必然带 Distinct 节点(MySQL 8.0.31+ 显示为 Using temporary; Using filesort);UNION ALL 就是纯 Append,数据流式输出,无等待。
影响不止是时间:
- 内存压力:UNION 默认用
sort_buffer_size(通常 256KB),超了就落盘,IO 拖慢整条链路 - 并发干扰:Distinct 阶段是单线程瓶颈,而 UNION ALL 的各分支可并行扫描
- 结果不可预测:UNION 不保证行序,除非显式加
ORDER BY;UNION ALL 严格按子句顺序拼接
什么时候非用 UNION 不可?
不是“要不要去重”,而是“业务是否允许重复”。比如:
- 合并用户表和管理员表查唯一登录账号 → 必须用
UNION,否则同一人出现在两个表里会算两次 - 查分库分表的订单数据,已知主键全局唯一 → 直接
UNION ALL,省掉去重开销 - ETL 场景中先
UNION ALL拉全量,再在外层用GROUP BY聚合 → 比在每个子查询里用UNION更可控
最容易被忽略的一点:INTERSECT 和 EXCEPT 在 MySQL 8.0.31+ 才支持,且内部也依赖 DISTINCT 逻辑——如果你真需要交集或差集,先确认版本,再评估能否用 JOIN + EXISTS 替代,往往更稳。










