UNION ALL 性能显著优于 UNION,因其跳过去重与排序开销,不建临时表、不比较、不排序;UNION 则强制去重并隐式排序,易触发磁盘临时表导致性能断崖下跌。

直接说结论:用 UNION 去重,用 UNION ALL 保全所有行且更快;选哪个不看“高级感”,只看你要不要重复数据、要不要排序、能不能接受性能损耗。
UNION 会自动去重并隐式排序,但代价是性能下降
执行 UNION 时,MySQL 实际做了三件事:合并结果 → 去重(基于所有列值完全一致)→ 默认按第一列升序排序(除非显式加 ORDER BY)。这意味着:
- 即使你没写
ORDER BY,UNION的结果也大概率不是原始查询的自然顺序 - 去重需要临时内存或磁盘排序,当单个结果集超过
sort_buffer_size时,性能会明显下滑 - 如果两个
SELECT中某列是VARCHAR(255)但实际只存 10 字符,比较时仍按完整定义比,可能误判为“不重复”(尤其涉及尾部空格或 collation 差异时)
UNION ALL 不做任何额外处理,快但必须自己兜底
UNION ALL 就是把多个结果集物理拼接,不做去重、不排序、不校验类型兼容性(只检查列数和基础类型是否可隐式转换)。常见踩坑点:
- 列名以第一个
SELECT的字段名为准,后面语句里的别名无效 —— 比如SELECT id AS user_id FROM t1 UNION ALL SELECT id AS order_id FROM t2,最终列名仍是user_id - 如果两个
SELECT的对应列一个是INT、一个是VARCHAR,MySQL 会尝试转成更宽泛类型(如DECIMAL或TEXT),可能引发隐式转换警告甚至截断 - 想整体排序?必须把整个
UNION ALL包进子查询再ORDER BY,不能只在某个分支后加
列数、类型、顺序必须严格一致,否则报错
这是硬性约束,不是建议。MySQL 要求每个 SELECT 的列数相同,对应位置的列类型“兼容”(比如 TINYINT 和 INT 可以,DATE 和 CHAR 通常不行),且顺序不能错位。例如:
SELECT id, name FROM users UNION ALL SELECT status, created_at FROM logs -- ❌ 报错:列类型不匹配,顺序也不对
正确写法必须对齐语义和类型:
SELECT id AS val, name AS label FROM users UNION ALL SELECT id AS val, title AS label FROM articles -- ✅ 列数、别名、类型都对齐
什么时候该强制用 UNION,哪怕慢一点
只有两种情况绕不开 UNION:
- 业务逻辑明确要求结果唯一,比如“查出所有活跃用户ID,不管他们出现在 orders 还是 carts 表里”
- 下游系统/应用代码依赖无序结果的稳定性(比如前端分页缓存了第 2 页数据,而你换
UNION ALL后因无排序导致翻页错乱)
除此之外,只要能接受重复、能自己加 ORDER BY 控制顺序,UNION ALL 几乎总是更优选择——尤其是当任一子查询返回上万行时,UNION 的去重开销可能让响应时间从 50ms 拉到 800ms。


















