union all性能远高于union,因其纯拼接、无去重排序;union等价于union all后加distinct和order by,需临时表、文件排序及可能磁盘i/o,100万行时慢11倍以上。

UNION ALL 不做去重,UNION 实际执行 DISTINCT + ORDER BY
数据库引擎处理 UNION 时,并不是“合并完再删重”这么简单。它等价于先用 UNION ALL 拼出全部结果,再套一层 SELECT DISTINCT ... ORDER BY。这意味着:必须把所有子查询结果暂存到临时表(Using temporary),为去重和排序触发 Using filesort,尤其在无索引列上代价极高;内存不足时还会写磁盘,I/O 成为瓶颈。而 UNION ALL 就是纯流式拼接——第一个结果集输出完,立刻接第二个,零哈希、零排序、零临时结构。
性能差距随数据量非线性放大
小数据量时差别不明显,但一过几万行,差距就陡增:
- 10 万行:
UNION ALL耗时约 82ms,UNION约 620ms(7.5 倍) - 100 万行:
UNION ALL810ms,UNION9200ms(11 倍以上)
这不是常数倍开销,而是因为去重排序本身是 O(n log n) 复杂度,且临时表写入、磁盘交换、CPU 比较都随数据量指数级加重。分页查询(LIMIT OFFSET)或嵌套子查询里,这个差距还会被进一步放大。
ORDER BY 必须写在外层,子查询里的无效
无论用哪个操作符,子查询中加 ORDER BY 都会被忽略(语法允许但无效果)。正确写法只有一种:
SELECT id, name FROM users_2023 UNION ALL SELECT id, name FROM users_2024 ORDER BY id;
否则你拿到的顺序不可控:UNION 的隐式排序可能按第一列升序,和你预期的插入时间、更新时间完全不一致;UNION ALL 则完全依赖子查询执行顺序,更难预测。显式外层 ORDER BY 是唯一可靠方式。
类型兼容和列对齐要求完全一致,别以为 UNION ALL 更宽松
两者对结构校验一视同仁,错一个就报错:
- 每个
SELECT的列数必须相等(SELECT a FROM t1和SELECT a,b FROM t2直接失败) - 对应位置列类型需兼容(
INT和VARCHAR在严格模式下会报错,UNION ALL同样报) - 列名永远以第一个
SELECT为准(SELECT id AS uid FROM t1 UNION ALL SELECT user_id AS id FROM t2,结果列名仍是uid) - NULL 处理有隐性差异:多个
NULL在UNION中被视为相同并去重,UNION ALL保留全部
最容易被忽略的是类型隐式转换失败场景——比如第一条语句返回 NOT NULL VARCHAR(50),第二条返回 NULL,某些 MySQL 版本会拒绝,不能靠 ALL 绕过。











