MySQL 5.7 后不建议用 UNION 替代 UNION ALL,因其强制去重触发临时表与排序、受 derived_merge 优化干扰导致执行计划不可控、掩盖数据异常且影响聚合准确性,而 UNION ALL 性能更优、语义更明确、行为更可预期。

MySQL 5.7 后不建议用 UNION 替代 UNION ALL,根本原因不是“语法过时”,而是它在多数业务场景下会主动引入性能陷阱和逻辑风险。
UNION 强制去重触发临时表和排序
只要用了 UNION,MySQL 就必须对整个合并结果做 DISTINCT 处理。即使你确认各子查询结果天然无交集(比如按不同状态分查订单),优化器也无法跳过这步——它得把所有行先拉到内存或磁盘临时表里,再排序+去重。
- EXPLAIN 中能看到
Using temporary; Using filesort,rows值常是各子查询之和的数倍 - 当单个子查询返回 50 万行,
UNION可能触发磁盘临时表(tmp_table_size/max_heap_table_size不够时),IO 成倍上升 -
UNION ALL则完全跳过这一步,数据流式合并,几乎没有额外开销
MySQL 5.7+ 的 derived_merge 优化让 UNION 更危险
5.7 默认开启 derived_merge=on,它会尝试把 UNION 子查询“合并”进外层查询优化。但这个优化对 UNION 很不友好:一旦合并失败或判断不准,就退回到低效执行路径,且 ORDER BY / LIMIT 极易被吞掉或错位。
- 常见现象:
SELECT * FROM (SELECT a FROM t1 UNION SELECT a FROM t2) t ORDER BY a LIMIT 10,实际返回可能乱序或超限 - 加
SELECT STRAIGHT_JOIN或设optimizer_switch='derived_merge=off'能缓解,但属于绕路方案 -
UNION ALL因结构更简单,受derived_merge影响小得多,行为更可预期
业务逻辑上,UNION ALL 更容易控制语义
用 UNION ALL 重写 OR 查询时,你是在显式声明“这几路数据互不重叠”,而 UNION 却在暗地里帮你做假设——它假设你真需要去重,且去重结果就是你要的业务含义。
- 例如查“已支付订单 OR 用户 ID 在白名单”,
UNION ALL返回重复订单时,是你自己决定是否在外层加DISTINCT或用GROUP BY聚合;UNION却强制抹掉重复,可能掩盖数据异常(比如同一订单被误插两次) - 聚合类场景(如成绩汇总)中,
UNION ALL是唯一能保证原始行数不丢失的方式;UNION会悄悄丢掉同 code+name+score 的重复行,导致 sum 错误 - 调试时,
UNION ALL的执行计划清晰分段,每路WHERE条件是否走索引一目了然;UNION的执行计划常混成一团,难定位哪路拖慢了整体
真正要警惕的不是 UNION ALL 本身,而是把它当成“万能 OR 替代品”后忽略字段对齐、NULL 处理和外层聚合逻辑——这些细节出错,比用错关键字影响更大。


















