unpivot是sql server专属列转行方案,适用于已知固定列名场景,可将多列摊平为attribute和value两列后配合group by聚合;需注意in子句括号、null值过滤及类型一致问题。

用 UNPIVOT 做列转行再聚合,但注意 SQL Server 专属
SQL Server 支持 UNPIVOT,是原生列转行方案,适合已知固定列名的场景。它把多个列“摊平”成两列:一列存原列名(attribute),一列存值(value),之后就能配合 GROUP BY 和聚合函数统计。
常见错误是漏写 IN 子句中列名的括号,或把字符串值误当列名传入;另外 UNPIVOT 会自动过滤 NULL 值,如果原始数据有空值且需保留,得先用 ISNULL 或 COALESCE 替换。
-
UNPIVOT只在 SQL Server 和 Azure SQL 中可用,PostgreSQL/MySQL 不支持 - 语法必须明确列出所有要转的列:
(value FOR attribute IN ([col1], [col2], [col3])) - 转出的
value列类型必须一致,否则会报错,必要时用CONVERT统一 - 示例:统计各科目总分
SELECT attribute AS subject, SUM(CAST(value AS INT)) AS total_score<br>FROM scores UNPIVOT (value FOR attribute IN ([math], [english], [science])) AS u<br>GROUP BY attribute
MySQL / PostgreSQL 用 UNION ALL 模拟列转行
没有 UNPIVOT 的数据库,靠多个 SELECT + UNION ALL 手动“拼”出行。虽然写法冗长,但兼容性好、逻辑透明,且能精确控制每列的处理方式(比如对某列做单位转换或条件过滤)。
容易踩的坑是字段数和类型不一致导致 UNION ALL 失败,尤其是混合了字符串和数字列时;另外别忘了加 WHERE 过滤掉不需要参与统计的原始行(如状态为无效的数据)。
- 每条
SELECT必须输出相同数量、同类型的列,建议显式CAST统一 - 用常量字符串标识来源列,例如
'math' AS subject,别用变量或表达式 - 避免在每个子查询里重复写复杂
JOIN或子查询,可先用 CTE 提取基础数据集 - 示例(MySQL):
SELECT 'math' AS subject, math AS score FROM exam WHERE status = 'valid'<br>UNION ALL<br>SELECT 'english', english FROM exam WHERE status = 'valid'<br>UNION ALL<br>SELECT 'science', science FROM exam WHERE status = 'valid'
再套一层GROUP BY subject即可聚合
用 JSON_TABLE(MySQL 8.0+)或 jsonb_array_elements(PostgreSQL)动态转列
当列名不固定、或来自配置表时,硬写 UNION ALL 维护成本高。MySQL 8.0+ 可先把多列构造成 JSON 对象,再用 JSON_TABLE 解析;PostgreSQL 则常用 jsonb_build_object + jsonb_each 实现类似效果。
这种方式灵活,但性能明显低于静态方案——每次都要解析 JSON 字符串,且无法利用原列上的索引。仅推荐用于列结构频繁变动、或真正需要“元数据驱动”的场景。
- MySQL 示例中,
JSON_OBJECT('math', math, 'english', english)构造对象后,JSON_TABLE的COLUMNS子句必须与键名严格匹配 - PostgreSQL 中
jsonb_each返回(key, value),value是jsonb类型,聚合前通常要::text::numeric转换 - 两种方式都要求目标列值能无损转成 JSON,含二进制、LOB 或特殊字符时需预处理
聚合前是否该先过滤?这直接影响结果和性能
列转行后行数会爆炸式增长(原表 1 行 → N 列 → N 行),如果在转行后再用 WHERE 过滤,等于对膨胀后的中间结果做计算,浪费资源。更优做法是在转行前就裁剪数据集。
比如只统计“2024 年度有效订单”,应该先 WHERE year = 2024 AND status = 'active',再做列转行;而不是把全部历史数据转完再筛。CTE 或子查询在这里不是语法糖,而是性能关键。
- 在
UNION ALL每个分支里重复写同样WHERE条件,不如提到外层统一过滤 - 使用
UNPIVOT时,WHERE必须放在UNPIVOT子句之前,否则无效 - JSON 方案中,构造 JSON 前就该完成业务过滤,避免把无效字段也塞进 JSON
UNION ALL 的啰嗦;而小表+SQL Server,UNPIVOT 最省心。别为了“看起来高级”硬上 JSON 方案——多数统计场景根本用不到那么强的灵活性。










