case when行转列本质是聚合+条件判断,必须配合group by和sum/max等聚合函数才能实现透视效果,否则结果膨胀、无法汇总。

SQL里用CASE WHEN做行转列,本质是聚合+条件判断
直接说结论:CASE WHEN本身不转列,必须配合GROUP BY和聚合函数(如SUM、COUNT、MAX)才能模拟Excel透视表的“按行分组、按列展开”效果。很多人写了CASE WHEN却没加GROUP BY,结果每行都出一条,根本不是透视效果。
典型场景:销售数据表sales含region(地区)、product(产品)、amount(金额),想按地区为行、产品为列汇总金额。
- 必须对
region分组:GROUP BY region - 每个产品对应一列,用
CASE WHEN product = 'A' THEN amount ELSE 0 END提取值 - 外层套
SUM()把同一地区的多条记录聚合成一个数 - 注意
ELSE 0不能省——否则SUM遇到NULL会跳过,导致结果偏小
写错CASE WHEN的三个高频错误
错误不是语法报错,而是结果明显不对,调试时容易绕弯:
-
CASE WHEN product = 'A' THEN amount END漏了ELSE 0→ 该产品不存在的行返回NULL,SUM后整列为NULL - 在
SELECT里写了CASE WHEN,但GROUP BY没包含所有非聚合字段 → MySQL 5.7+或严格模式直接报错:Expression #1 of SELECT list is not in GROUP BY clause - 用
MAX(CASE...)代替SUM(CASE...)处理数值型指标 → 没问题;但若字段本身是字符串(如状态码),且一行多值,MAX取字典最大值,可能掩盖真实分布
MySQL / PostgreSQL / SQL Server三者的细微差别
逻辑一致,但默认行为和语法容错性不同:
- MySQL 8.0+支持
PIVOT语法,但兼容性差,生产环境仍推荐CASE WHEN + GROUP BY - PostgreSQL对
GROUP BY要求最严:所有SELECT中的非聚合字段必须显式出现在GROUP BY里 - SQL Server允许
SELECT中出现未分组的列(只要它函数依赖于分组键),但开启ANSI_WARNINGS ON后会警告,建议统一显式写出 - 三者都支持
COALESCE(SUM(...), 0)兜底,避免前端显示NULL
当列名是动态的(比如产品种类每周变),CASE WHEN就不够用了
硬编码CASE WHEN product = 'A'、CASE WHEN product = 'B'只能应对固定维度。一旦产品列表来自另一张表或随时间增减,就得用动态SQL:
- MySQL:拼接字符串+
PREPARE/EXECUTE,注意SQL注入风险,product值必须预清洗 - PostgreSQL:用
EXECUTE+format(),可结合string_agg()生成列定义 - 更稳妥的做法是把透视逻辑移到应用层(Python/Pandas或BI工具),SQL只负责拉宽表原始数据
真正难的从来不是写对那几行CASE WHEN,而是想清楚:这个“列”到底该不该由SQL来定——如果业务上它本就该灵活变动,那就别在SQL里钉死。











