case when 是行转列最稳的写法,因其是 sql 标准语法,全库兼容,需配合 group by 与聚合函数使用,注意 null 处理、类型一致性和分组字段完整性。

为什么 CASE WHEN 是行转列最稳的写法
因为它是 SQL 标准语法,所有主流数据库(MySQL、PostgreSQL、SQL Server、Oracle)都支持,且不依赖窗口函数或特定扩展。用 PIVOT 或 JSON_OBJECTAGG 看似简洁,但换库就可能报错或行为不一致。
核心逻辑是:把「分组维度」和「要展开的类别」拆开——原表每行代表一个事实,CASE WHEN 把每个类别的值“投射”到对应列上,再用 SUM/MAX 等聚合收口。
- 必须配合
GROUP BY,否则聚合结果会坍缩成单行 -
CASE WHEN内部不能直接写聚合函数,比如CASE WHEN status='paid' THEN COUNT(*)是错的;得写成SUM(CASE WHEN status='paid' THEN 1 ELSE 0 END) - 如果某类别在某个分组中完全不存在,对应列会是
NULL,不是 0 —— 需要用COALESCE或IFNULL显式兜底
对比不同分组时,GROUP BY 到底该写哪些字段
关键看你想比什么:如果比「每个部门下各状态订单数」,GROUP BY dept_name 就够了;如果还要按年份切分,就得加 YEAR(order_time)。
常见错误是漏掉非聚合字段,导致 MySQL 5.7+ 报 ERROR 1055,或者 PostgreSQL 直接拒绝执行。
- 所有出现在
SELECT中、又没被聚合函数包裹的字段,必须出现在GROUP BY里 - 别用
GROUP BY 1,2这种序号写法——可读性差,改 SELECT 顺序就崩 - 如果分组键本身有空值,
GROUP BY会把所有NULL归为一组,注意是否符合业务预期
SUM vs COUNT vs MAX:选哪个聚合函数
取决于你投射的值类型和语义。不是所有场景都能用 COUNT,也不是 SUM 一定比 MAX 安全。
- 统计数量(如「已支付订单数」):用
SUM(CASE WHEN status='paid' THEN 1 ELSE 0 END),比COUNT(CASE WHEN status='paid' THEN 1 END)更直观,且不会因NULL被跳过 - 取最新值(如「各品类最近上架时间」):用
MAX(CASE WHEN category='book' THEN listed_at END) - 布尔判断(如「是否有退款订单」):用
MAX(CASE WHEN refund_id IS NOT NULL THEN 1 ELSE 0 END),避免BOOL类型兼容问题
容易被忽略的 NULL 和类型隐式转换
CASE WHEN 每个分支返回值类型不一致时,数据库会强制转成统一类型——MySQL 可能转成字符串,PostgreSQL 会报错。而 ELSE 分支缺失时,整个表达式默认补 NULL,但 NULL 和数字做运算还是 NULL。
- 显式写
ELSE 0或ELSE '',别依赖默认 - 数值类列统一用
0,时间类用NULL或最小合法时间(如'1970-01-01'),别混用 - 测试时故意构造一条全为
NULL的分组数据,看结果列是不是全NULL—— 这往往是ELSE缺失或类型不匹配的信号
复杂点在于,当分组维度多、类别多时,CASE WHEN 块会迅速变长,但这是可控的冗余;比起动态 SQL 或应用层拼接,它更易审计、更少出错。










