UNION和UNION ALL核心区别在于去重与性能:UNION自动去重并隐式排序,开销大;UNION ALL仅物理堆叠,无去重排序,速度快2–5倍,且NULL视为相等;二者均要求列数、顺序、类型严格对齐,否则报错或数据错位。

UNION 不是万能的“拼表”工具,它只做行堆叠,且对列结构极其敏感——列数、顺序、类型三者必须严格对齐,否则直接报错或数据错位。
UNION 和 UNION ALL 的核心区别在哪
两者都要求左右查询返回相同数量的列,但行为完全不同:
-
UNION会自动去重,等价于在合并后加了DISTINCT,性能开销明显更高; -
UNION ALL只做物理堆叠,不查重、不排序,速度通常快 2–5 倍; - NULL 值在
UNION中被视为相等,所以(1, NULL)和(1, NULL)会被合并为一行; - 若业务上明确知道无重复(比如按时间分区的订单表),或重复本身可接受(如日志归档),优先用
UNION ALL。
列名、数据类型和顺序怎么对齐才不出错
结果集的列名永远以第一个 SELECT 语句为准,后续查询的列名被忽略;但数据类型由 SQL Server 的隐式转换规则决定,容易踩坑:
- 如果第一个查询中某列为
VARCHAR(10),第二个为VARCHAR(50),最终列类型取更宽的VARCHAR(50); - 但如果一个是
INT,另一个是VARCHAR,SQL Server 会尝试隐式转换——失败则报错Conversion failed when converting the varchar value 'abc' to data type int.; - 列顺序必须一致:不能第一个查
id, name,第二个写name, id,否则值会错位; - 字段缺失时需“补空”:用
NULL AS column_name或'' AS column_name对齐结构,不能靠“省略”蒙混过关。
ORDER BY 和 TOP/LIMIT 怎么放才有效
ORDER BY 和 TOP(或 LIMIT)只能出现在整个 UNION 语句的末尾,不能写在子查询里:
- 错误写法:
SELECT TOP 10 * FROM t1 ORDER BY id UNION SELECT * FROM t2→ 报错; - 正确写法:
(SELECT * FROM t1) UNION ALL (SELECT * FROM t2) ORDER BY id; - 若真需要对某一部分先取前 N 行再合并,必须把那部分封装成子查询:
(SELECT TOP 10 * FROM t1 ORDER BY created_at) UNION ALL SELECT * FROM t2; - 注意:子查询里的
ORDER BY单独存在无意义,必须配合TOP或OFFSET/FETCH才生效。
UNION 和 JOIN 容易混淆的边界在哪
这是最常被误用的点:UNION 是纵向堆叠(行合并),JOIN 是横向拼接(列合并):
- 想把“华东客户”和“华北客户”的名单合为一张总表?用
UNION; - 想查“每个订单 + 对应客户姓名”?必须用
JOIN,UNION在这里完全无效; - 两个表结构不同但想强行合并?不是改写法的问题,而是该反思是否真该用 UNION —— 比如把用户表和日志表硬堆一起,往往意味着模型设计有偏差;
- 真正需要混合结构时,应优先考虑
UNION ALL+ 显式类型转换(CAST或CONVERT)+ 统一别名,而不是依赖隐式行为。
实际写 UNION 时,最容易被忽略的是列类型的隐式转换边界和 ORDER BY 的作用域限制——它们不会报语法错,但会导致结果错乱或性能骤降,而且问题往往在线上跑了一周才暴露。

















