unpivot仅在sql server(2005+)和oracle(11g+)中原生支持,mysql、postgresql、sqlite均不支持;要求in子句中所有列数据类型完全一致,否则报错,且默认过滤null行。

UNPIVOT 不是万能的“一键转换”,它只在 SQL Server 和 Oracle 11g+ 中原生可用,且对数据类型极其敏感——列类型不一致时直接报错,不会自动转成 VARCHAR。
UNPIVOT 在哪些数据库里能用?
只有 SQL Server(2005+)和 Oracle(11g 及以上)原生支持 UNPIVOT。MySQL、PostgreSQL、SQLite 均无该语法,硬写会报 Incorrect syntax near 'UNPIVOT' 或类似错误。
- SQL Server 示例可直接运行:
SELECT name, subject, score FROM studentscores UNPIVOT (score FOR subject IN (chinese, math, english)) AS u; - Oracle 写法几乎一致,但要求列名不能含中文或特殊字符(除非加双引号),且必须显式指定别名:
... UNPIVOT (score FOR subject IN ("微积分" AS '微积分', "线性代数" AS '线性代数')) - MySQL 用户别折腾了,
UNPIVOT不是预留关键字,也进不了未来版本计划
为什么一用就报 “type mismatch” 错误?
因为 UNPIVOT 要求括号内所有列(如 chinese, math, english)必须是**完全相同的数据类型**,连精度都不能差——INT 和 TINYINT 不行,VARCHAR(10) 和 VARCHAR(20) 也不行。
- 最稳妥的解法:在子查询中统一强转,例如:
SELECT name, chinese_i, math_i, english_i FROM (SELECT name, CAST(chinese AS INT) AS chinese_i, CAST(math AS INT) AS math_i, CAST(english AS INT) AS english_i FROM studentscores) t - 千万别依赖隐式转换——
UNPIVOT比SELECT更严格,哪怕只是NULL列类型推导失败也会中断 - 如果原始列含
NULL,UNPIVOT默认会跳过整行(不是留 NULL),这点和UNION ALL行为不同
IN 子句列名写错或动态变化怎么办?
IN (chinese, math, english) 是硬编码,列名增减必须手动改 SQL。一旦字段来自配置表或前端传参,这条路就走不通。
- 列名不确定时,
UNPIVOT必须放弃,改用UNION ALL拼接(虽然啰嗦但可控)或 JSON 函数(如 SQL Server 的OPENJSON+WITH) - Oracle 可用
DBMS_SQL动态拼 SQL,但运维成本高,上线前务必测试 NULL 处理逻辑 - SQL Server 2016+ 可考虑
STRING_AGG+ 动态 SQL,但要注意权限限制(EXECUTE AS上下文可能无权建临时表)
真正难的从来不是写对那一行 UNPIVOT,而是确认源表列是否真的一致、目标库是否真的支持、以及业务是否允许丢弃 NULL 行——这三个点漏掉任何一个,结果都和预期对不上。










