UNPIVOT 更简洁语义清晰但仅限 SQL Server 和 Oracle;跨库兼容必须用 UNION ALL,需注意数据类型对齐、NULL 处理及避免重复扫描。

直接说结论:用 UNPIVOT 更简洁、语义清晰,但只支持 SQL Server 和 Oracle;跨数据库兼容必须用 UNION ALL 拆行,且要注意数据类型对齐和 NULL 处理。
UNPIVOT 语法怎么写?为什么常报错?
UNPIVOT 是把宽表(多列)转长表(多行)的专用操作,但它不是“自动识别列”,必须显式列出源列名和目标列名。
- 常见错误是把非标量列(如
id)也放进UNPIVOT的FOR ... IN子句里——这会报Incorrect syntax near 'FOR' - 所有被 unpivot 的列必须是相同数据类型,否则会触发隐式转换失败,比如
INT和VARCHAR混用时需提前CAST - 原表中为
NULL的单元格,在结果里直接被过滤掉,不会生成对应行(这点和UNION不同)
示例(SQL Server):
SELECT id, attribute, value
FROM (
SELECT id, CAST(age AS VARCHAR(10)) AS age,
CAST(score AS VARCHAR(10)) AS score
FROM students
) AS src
UNPIVOT (value FOR attribute IN (age, score)) AS up;
UNION ALL 替代方案:怎么避免类型不匹配和重复扫描?
当用在 MySQL、PostgreSQL 或需要兼容多库时,UNION ALL 是唯一可行方式。它本质是“手动拼行”,每列单独查一次再合并。
- 必须统一各
SELECT子句的列数、顺序和数据类型,推荐全部转成TEXT或VARCHAR避免隐式转换失败 - 别用
UNION(去重),除非真要删重——它会强制排序去重,性能差很多;一律用UNION ALL - 如果原表很大,每个
SELECT都会全表扫描一次,可考虑加覆盖索引或先存到临时表再查
示例(通用 SQL):
SELECT id, 'age' AS attribute, CAST(age AS VARCHAR(20)) AS value FROM students UNION ALL SELECT id, 'score' AS attribute, CAST(score AS VARCHAR(20)) AS value FROM students;
NULL 值处理差异:UNPIVOT 丢行,UNION ALL 保留
这是实际业务中最容易踩坑的一点:同一行中某列是 NULL,两种方法行为完全不同。
-
UNPIVOT默认跳过该列对应行(即不输出(id, 'age', NULL)) -
UNION ALL会照常输出,值为NULL—— 如果你依赖 NULL 行做后续统计或 JOIN,这点必须提前确认 - 若坚持用
UNPIVOT但又想保留 NULL 行,得先用ISNULL()或COALESCE()填默认值(如空字符串),再 unpivot
真正难的不是写出语句,而是判断哪一列可能为 NULL、哪些系统不支持 UNPIVOT、以及下游是否依赖空值占位——这些细节往往上线后才暴露。

















