union 等价于 union all 加 distinct 和隐式 order by,需临时表与文件排序,性能差;union all 流式输出、无去重排序,性能优异;两者在列数、类型兼容、列名、null 处理上要求一致,order by 必须写在外层。

UNION 实际执行的是 UNION ALL + DISTINCT + ORDER BY
MySQL 并不会先拼接再删重,而是把 UNION 当作一个整体语义:必须返回唯一、有序的结果集。所以它等价于先用 UNION ALL 拼出全部数据,再套一层 SELECT DISTINCT ... ORDER BY。这意味着:必须把所有子查询结果暂存到临时表(Using temporary),为去重和排序触发 Using filesort——哪怕你没写 ORDER BY,MySQL 8.0 默认也会加隐式排序。一旦中间结果撑爆内存(超过 tmp_table_size 或 max_heap_table_size 中较小值),临时表就会落盘成 MyISAM 表,I/O 成倍上涨。
UNION ALL 是纯流式输出,零中间结构
UNION ALL 不建临时表、不排序、不哈希比对,子查询各自执行完就直接发给客户端。第一个结果集输出完毕,第二个立刻接上,整个过程无状态、无缓冲、无额外 CPU 比较。实测中,100 万行合并时:UNION ALL 耗时约 810ms,UNION 高达 9200ms——不是常数倍慢,而是因为去重排序本身是 O(n log n) 复杂度,且磁盘交换、内存争抢、CPU 比较全随数据量非线性加重。
列对齐、类型兼容、NULL 处理,两者一视同仁
别以为 UNION ALL 更“宽松”,它和 UNION 对结构校验完全一致:
- 每个
SELECT的列数必须相等,否则直接报错 - 对应位置列类型需兼容,
SELECT 1 UNION ALL SELECT '1'在严格模式下同样失败 - 列名永远以第一个
SELECT为准,SELECT id AS uid FROM t1 UNION ALL SELECT user_id AS id FROM t2返回列名仍是uid -
NULL处理有隐性差异:多个NULL在UNION中被视为相同并去重,UNION ALL保留全部
ORDER BY 必须写在外层,子查询里的无效
无论用哪个操作符,子查询中加 ORDER BY 都会被忽略(语法允许但无效果)。正确写法只有一种:SELECT id, name FROM users_2023 UNION ALL SELECT id, name FROM users_2024 ORDER BY id。否则顺序不可控:UNION 可能按第一列升序排,UNION ALL 完全依赖子查询执行顺序——而执行顺序本身受优化器、索引、并发等因素影响,无法保证稳定。











