union要求各select列数、类型、顺序严格一致,结果集列名以第一个select为准;嵌套时必须加别名,order by和top需在派生表外使用,优先选union all提升性能。

UNION必须整个包在派生表括号里
直接写 SELECT * FROM (SELECT a FROM t1 UNION SELECT a FROM t2) 会报错,因为 SQL Server(及多数主流数据库)要求派生表必须有别名。括号内是完整查询单元,UNION 是其中一部分,但整个子查询不能裸奔。
正确写法是给括号整体加别名,哪怕只是 AS t:
SELECT * FROM ( SELECT id, name FROM users_2025 UNION ALL SELECT id, name FROM users_2026 ) AS merged_users;
漏掉 AS merged_users 或只写 merged_users(不带 AS)在某些版本中也会失败,尤其在 SQL Server Compact 或旧版兼容模式下。
列对齐错误常发生在字段缺失或类型隐式转换失败时
两个 SELECT 的列数、顺序、类型必须兼容——这不是“尽量一致”,而是强制校验点。常见翻车场景:
- 第一个查询返回
id INT, name VARCHAR(20),第二个少写一列,比如只写name,直接报错Msg 205, Level 16: All queries in a UNION must have the same number of expressions - 第一个查
amount DECIMAL(10,2),第二个用字符串'N/A'填充,SQL Server 尝试转成 numeric 失败,抛出Conversion failed when converting the varchar value 'N/A' to data type decimal - 用
NULL占位时没显式指定类型:NULL AS amount在第一个查询是DECIMAL时,可能被推导为INT,导致后续计算出错
稳妥做法是显式 cast 或用同类型默认值:
SELECT id, name, amount FROM sales_q1 UNION ALL SELECT id, name, CAST(NULL AS DECIMAL(10,2)) AS amount FROM sales_q2_missing;
ORDER BY 和 TOP 只能在派生表外生效
想先取每个子集的前 5 条再合并?不能这么写:
SELECT * FROM ( SELECT TOP 5 * FROM t1 ORDER BY created_at DESC UNION ALL SELECT TOP 5 * FROM t2 ORDER BY created_at DESC ) AS t;
语法错误:子查询里的 ORDER BY 必须配合 TOP、OFFSET/FETCH 才合法,但这里 UNION 把两个带 ORDER BY 的子句连起来了,解析器直接拒掉。
真正可行的路径只有两条:
- 把排序逻辑压进子查询内部,并用
TOP封装:(SELECT TOP 5 * FROM t1 ORDER BY created_at DESC) - 合并后再统一排序:
SELECT * FROM (...) AS t ORDER BY created_at DESC
注意:第二种方式的 ORDER BY 是作用于整个合并结果,不是各子集分别排。
UNION ALL 在嵌套中性能优势会被放大
在派生表里用 UNION,数据库不仅要堆叠行,还要做去重 + 隐式排序;而 UNION ALL 就是纯追加。当嵌套层级变深(比如三层派生表都含 UNION),开销会指数级增长。
除非业务明确要求去重,否则嵌套场景下优先选 UNION ALL。一个容易被忽略的细节是:NULL 值在 UNION 中视为相等,所以两行都是 (1, NULL) 会被合并;但 UNION ALL 不管这个,照单全收——这既是性能来源,也是数据语义差异点。










