union all纯流式拼接,不触发去重和排序;union强制等价于select distinct * from (union all)并隐式排序,必走临时表与文件排序,性能呈非线性陡增。

UNION ALL 不触发去重和排序,UNION 必须走 DISTINCT + ORDER BY 流程
UNION ALL 的执行路径就是纯流式拼接:子查询 A 输出一行,立刻发给客户端;A 完了,B 紧接着输出,中间不缓存、不比较、不建临时结构。而 UNION 在 MySQL 内部等价于 SELECT DISTINCT * FROM (subquery1 UNION ALL subquery2) ORDER BY ... —— 即使你没写 ORDER BY,MySQL 8.0+ 也会加隐式排序保证结果唯一性。这强制触发 Using temporary 和 Using filesort,内存不够时直接落盘成 MyISAM 临时表,I/O 成瓶颈。
大数据量下性能差距不是线性,而是非线性陡增
小数据(几万行)时差别不明显,但一旦过阈值,开销指数级加重:
- 10 万行:
UNION ALL约 82ms,UNION约 620ms(7.5 倍) - 100 万行:
UNION ALL约 810ms,UNION高达 9200ms(11 倍以上)
根本原因在于去重排序是 O(n log n) 复杂度,且涉及临时表写入、磁盘交换、CPU 比较三重压力。嵌套在视图或分页查询(LIMIT OFFSET)里,这个差距还会被进一步放大。
列对齐、类型兼容、NULL 处理,UNION 和 UNION ALL 要求完全一致
别以为换 UNION ALL 就能绕过结构校验。两者都严格要求:
- 每个
SELECT的列数必须相等,否则报错ERROR 1222 - 对应位置列类型需兼容,
SELECT 1 UNION ALL SELECT '1'在严格模式下同样失败 - 列名永远以第一个
SELECT为准,后续的AS别名无效 -
NULL在UNION中被视为相等并去重,UNION ALL保留全部——这可能暴露你没意识到的空值分布问题
ORDER BY 只能写在外层,子查询里的写法无效且误导
下面这种写法是错的,MySQL 会忽略两个子查询里的 ORDER BY:
SELECT id FROM t1 ORDER BY create_time DESC UNION ALL SELECT id FROM t2 ORDER BY create_time DESC;
正确方式只有一种:
SELECT id FROM t1 UNION ALL SELECT id FROM t2 ORDER BY id;
但要注意:一旦加了外层 ORDER BY,UNION ALL 也会触发排序,性能优势就打折扣了。所以更优策略是:在子查询里靠索引保证局部有序,再用外层 ORDER BY 最小化排序范围。
真正容易被忽略的是:UNION 的性能衰减是非线性的,且无法靠调优参数绕过——它由底层机制决定。是否用 UNION,只取决于业务是否真需要去重;如果只是图省事默认选它,等于主动给自己埋雷。











