union会去重,union all不会:union自动执行distinct,对所有列完全相同的行(含null)只保留一行;union all仅物理拼接,不判重、不排序、零额外开销。

UNION 会去重,UNION ALL 不会
这是最核心的区别。UNION 在合并多个 SELECT 结果时,会自动执行去重(DISTINCT),只要两行所有列值完全相同(NULL 也视为相等),就只保留一行;而 UNION ALL 完全不比较、不判重,只是把结果按顺序堆叠——哪怕全是重复行,也会原样输出。
常见错误现象:UNION 查询比预期少行,尤其是跨表关联或含 NULL 字段时;UNION ALL 返回大量重复,但执行快得多。
- 去重逻辑依赖数据库实现:MySQL 通常用排序去重,PostgreSQL 可能用哈希,都额外消耗 CPU 和内存
-
NULL = NULL在 UNION 中被判定为“重复”,但在某些业务场景下这未必符合语义(比如日志表中两条NULL状态的记录实际代表不同事件) - 若你已通过
WHERE条件或分片逻辑确保无交集(如按日期拆分的订单表),用UNION就是纯浪费
列数、类型、顺序必须严格一致
无论用 UNION 还是 UNION ALL,所有 SELECT 子句的列数必须相等,对应位置的列类型需兼容,否则直接报错:
ERROR 1222 (HY000): The used SELECT statements have a different number of columns
类型兼容性差异明显:
- MySQL 对隐式转换较宽松:例如
SELECT 1 UNION ALL SELECT '1'可能成功,但数值精度丢失(字符串转数字后截断) - PostgreSQL 更严格:
SELECT 1 UNION ALL SELECT '1'直接失败,必须显式CAST或统一类型 - 排序规则(
COLLATE)不一致也会报错,尤其跨库或字段定义不同时,需在任一子句中加COLLATE utf8mb4_unicode_ci
性能差距可达 10 倍以上
实测中,百万级数据合并时,UNION ALL 比 UNION 快一个数量级。因为 UNION 需建临时结构(排序或哈希表)做全量比对,而 UNION ALL 是纯流式拼接,零额外计算。
容易踩的坑:
- 在 MyBatis 或其他 ORM 中写
UNION却没意识到它被高频调用,拖慢整体接口响应 - 误以为“去重更安全”,却忽略源表主键/业务键天然不重(如按
created_at分片的日志表),强行加UNION反而引入瓶颈 - 用
ORDER BY时,它作用于最终合并结果,不是每个子查询——想对每个子查询单独排序?得用括号包裹并配合LIMIT,否则语法报错
列名和别名以第一个 SELECT 为准
UNION 和 UNION ALL 的结果集列名永远取自第一个 SELECT 子句,后续子句中的别名无效。例如:
SELECT id AS user_id, name FROM users UNION ALL SELECT uid AS id, full_name AS name FROM contractors;
结果列名仍是 user_id 和 name,不是 id 或 full_name。
关键细节:
- 若第一个
SELECT某列为表达式(如COALESCE(email, phone)),又没加别名,结果列名可能为COALESCE(email, phone),导致应用层取值失败 - 各子句字段顺序错位(如前一个选
id, name,后一个写name, id)会导致数据错列,且不易察觉 - PostgreSQL 要求所有子句字段类型完全一致,不能靠隐式转换兜底,这点比 MySQL 更“较真”
UNION ALL 应该是默认选择——不是因为它“更高级”,而是因为多数时候,去重是冗余操作。










