行转列靠case when+聚合函数实现,核心是分组后条件聚合;必须用group by,max()最稳妥;null处理需在聚合内用coalesce;动态列需应用层拼sql或数据库特有语法。

用CASE WHEN + 聚合函数做行转列,核心是“分组+条件聚合”
行转列不是SQL标准语法,而是靠 CASE WHEN 搭配 MAX()、SUM() 等聚合函数模拟出来的。关键在于:先按需分组(比如按用户ID),再对每个分组内不同类别的值,用 CASE WHEN 单独提取并聚合——因为同一组里每种类别最多只有一条记录,所以用 MAX() 或 MIN() 都能安全取值。
常见错误是漏写 GROUP BY,导致结果只有一行;或者 CASE WHEN 没覆盖全类别,漏掉的值变成 NULL。
- 必须搭配
GROUP BY,分组字段通常是原表中“不变的维度列”,如user_id、order_date -
CASE WHEN的ELSE可省略,但建议显式写成ELSE NULL,避免隐式类型转换问题 - 聚合函数选
MAX()最稳妥;若字段允许重复且要累加(如销量),才用SUM()
MySQL/PostgreSQL/SQL Server 通用写法示例
假设有一张销售记录表 sales,字段为 user_id、product_type、amount,要把 product_type 的值('A'、'B'、'C')转成三列:
SELECT user_id, MAX(CASE WHEN product_type = 'A' THEN amount END) AS amount_A, MAX(CASE WHEN product_type = 'B' THEN amount END) AS amount_B, MAX(CASE WHEN product_type = 'C' THEN amount END) AS amount_C FROM sales GROUP BY user_id;
注意:MySQL 8.0+ 和 PostgreSQL 支持 FILTER (WHERE ...) 语法替代 CASE WHEN,但兼容性差,不推荐在跨库场景使用。
遇到NULL值或空字符串时怎么处理?
如果原始数据中 amount 本身是 NULL,上面写法会把整列设为 NULL,但有时你希望显示 0。这时候不能直接在 CASE WHEN 外套 COALESCE(),而要包在聚合函数内部:
MAX(COALESCE(CASE WHEN product_type = 'A' THEN amount END, 0)) AS amount_A
否则 COALESCE(MAX(...), 0) 会在聚合后补 0,掩盖了“该用户根本没买过 A 类产品”的事实。
- 想区分“买了但金额为 NULL”和“根本没买”,就别用
COALESCE - 想统一显示 0,就把
COALESCE放在CASE WHEN的THEN分支里,或整个CASE表达式内部 - PostgreSQL 中还可配合
NULLS LAST控制排序,但行转列一般不涉及排序
动态行转列为什么不能只靠SQL?
上面所有例子都要求你知道 product_type 的全部取值(A/B/C)。一旦新增品类 D,就得手动改 SQL —— 这就是硬编码的代价。纯 SQL 无法自动发现新列名,因为列结构必须在查询编译前确定。
真正需要动态行转列时,得靠应用层拼接SQL(如Python用 SELECT ... FROM sales GROUP BY user_id 先查出所有 product_type,再生成带 N 个 CASE 的语句),或数据库特定功能(如SQL Server的 PIVOT,但依然要预定义列名)。
别指望一个SQL解决所有场景,动态列本质是元数据问题,SQL只是执行工具。











