union去重且隐式排序,union all不去重不排序;前者性能低但确保唯一性,后者性能高但需业务确认重复是否合理。

UNION 和 UNION ALL 都能合并多个 SELECT 结果,但它们的行为差异直接决定你查出来的数据对不对——尤其是去重逻辑和性能表现。
UNION 会自动去重,但代价是隐式执行 DISTINCT
当你写 SELECT ... UNION SELECT ...,数据库实际做了三件事:执行两个子查询 → 合并所有行 → 对整个结果集跑一次 DISTINCT。这意味着:
- 即使两组数据本身无重复(比如按日期分区查昨天和今天),
UNION仍会扫描全部结果做比较 -
NULL被视为相等,所以两行只有NULL字段不同也会被去重 - 列名以第一个
SELECT的别名为准,后续子查询的AS别名会被忽略 - 如果某列是表达式(如
FirstName + ' ' + LastName),各子查询必须保持完全一致,否则类型推导可能失败
UNION ALL 不去重,但要求你提前确认业务逻辑隔离
UNION ALL 只做拼接,不检查重复,所以快。但它不会帮你判断“该不该重复”:
- 用它合并主表和备份表?同一用户很可能在两边都存在 → 下游统计翻倍
- 查不同状态的数据(如
status = 'active'和status = 'archived')?天然隔离,UNION ALL更安全 - 字段顺序和类型必须严格一致;比如
INT和VARCHAR拼接时,SQL Server 会尝试隐式转换,但 PostgreSQL 可能直接报错UNION types integer and text cannot be matched
ORDER BY 必须写在外层,子查询里加了也白加
这是最容易踩的坑:SELECT ... ORDER BY x UNION SELECT ... 中的 ORDER BY 会被忽略(除非配合 LIMIT 或窗口函数)。正确写法是:
SELECT customerid, companyname, FirstName + ' ' + LastName AS Name FROM saleslt.Customer WHERE customerid BETWEEN 1 AND 9 UNION ALL SELECT customerid, companyname, FirstName + ' ' + LastName AS Name FROM saleslt.Customer WHERE customerid BETWEEN 10 AND 19 ORDER BY customerid;
注意:ORDER BY 引用的列名必须出现在最终结果集中,不能用子查询里的别名(比如不能写 ORDER BY Name 如果第一个 SELECT 没定义 Name)。
列数、顺序、类型兼容性是硬门槛,不是可选项
报错 All queries in a UNION must have the same number of columns 说明列数不匹配;而类型问题更隐蔽:
- SQL Server 允许
INT和CHAR拼接(转成字符串),但 PostgreSQL 要求显式::TEXT - 两个子查询都有
DATE列,但一个是DATE类型、另一个是DATETIME2(0),某些版本会拒绝执行 - 建议统一用显式转换:比如都写
CAST(created_at AS DATE),避免依赖数据库默认行为
真正麻烦的从来不是语法写不对,而是没想清楚“这些数据到底该不该合并”,以及“重复到底是脏数据还是业务事实”。UNION 和 UNION ALL 只是工具,选哪个取决于你对数据语义的理解,而不是性能或习惯。










