UNION会自动去重并隐式排序,UNION ALL仅拼接结果、不去重不排序;100万行时UNION可能慢11倍以上,因需临时表、排序和去重;二者均要求列数、类型、顺序严格一致。

UNION和UNION ALL的区别到底在哪
关键不是“哪个更好”,而是“要不要去重”。UNION 会自动去重 + 排序(隐式 ORDER BY),UNION ALL 完全不处理重复,也不排序,只是把结果集拼起来。实际查 100 万行数据时,UNION 可能慢几秒甚至几十秒——它背后在做 DISTINCT + SORT;而 UNION ALL 几乎是线性时间。
- 如果你确定两个结果集没有重复(比如查不同日期的订单、不同状态的用户),直接用
UNION ALL - 如果业务上允许重复(比如日志归档、审计追踪),也该用
UNION ALL - 只有当你明确需要“合并后唯一且有序”才选
UNION,但更推荐显式写SELECT ... UNION ALL ... ORDER BY控制排序逻辑
列数、类型、顺序必须严格一致
UNION 和 UNION ALL 不是“把表拼起来”,而是“把查询结果的行拼起来”。所以:
- 两边
SELECT的列数必须相等,少一列或多数都会报错:ERROR: each UNION query must have the same number of columns - 对应位置的列类型最好兼容(如
INT和BIGINT通常可隐式转换,但TEXT和JSONB在 PostgreSQL 中就不行) - 别依赖列名:最终结果集的列名取自第一个
SELECT,后面语句的别名会被忽略
SELECT id, name FROM users UNION ALL SELECT user_id AS uid, full_name FROM customers;
这不会报错,但第二列实际叫 name(来自第一句),full_name 的别名无效。
ORDER BY 和 LIMIT 只能加在最后
不能给每个子查询单独加 ORDER BY 或 LIMIT,SQL 会直接报错:ERROR: ORDER BY in a union query is not allowed(PostgreSQL)或类似提示(MySQL/SQL Server 各有语法限制)。
- 所有排序必须写在最后一个子句之后,作用于整个联合结果
- 如果真要分别取 Top N 再合并,得用子查询或 CTE 包一层
(SELECT id, score FROM exam WHERE subject = 'math' ORDER BY score DESC LIMIT 3) UNION ALL (SELECT id, score FROM exam WHERE subject = 'english' ORDER BY score DESC LIMIT 3);
上面这段在多数数据库里会报错。正确写法是:
SELECT * FROM ( SELECT id, score, 'math' AS subject FROM exam WHERE subject = 'math' UNION ALL SELECT id, score, 'english' AS subject FROM exam WHERE subject = 'english' ) t ORDER BY score DESC LIMIT 6;
NULL 值和隐式类型转换容易踩坑
UNION 去重时,NULL = NULL 被视为 true(符合 SQL 标准),所以两行只有 NULL 字段不同的记录会被合并掉——这点常被忽略。
- 比如
SELECT 1, NULL UNION SELECT 1, NULL结果只有一行 - 但
SELECT 1, NULL UNION ALL SELECT 1, NULL就是两行
类型隐式转换也可能导致意外行为:
- MySQL 中
'123'(字符串)和123(整数)在UNION里会被转成数字比较,可能丢失前导零或格式 - SQL Server 对
VARCHAR长度敏感:SELECT CAST('a' AS VARCHAR(1)) UNION SELECT CAST('ab' AS VARCHAR(2))会失败,因为目标长度取较短者
如果字段含义相同但类型不一致,显式用 CAST 或 CONVERT 统一更安全。
实际用的时候,先跑一遍 UNION ALL 看原始数据分布,再决定是否加 UNION 或额外去重逻辑——很多所谓“需要去重”的场景,其实是上游数据本就不该重复,该修的是写入逻辑,不是靠 UNION 擦屁股。











