sum(case...)能替代pivot,因其是ansi标准、跨数据库兼容的行转列方案:用case构造虚拟列并sum聚合,支持计数或求和,需注意null处理、group by完整性及避免case中函数/子查询影响性能。

为什么 SUM(CASE...) 能替代 PIVOT?
因为标准 SQL(尤其 MySQL、PostgreSQL、SQLite)不原生支持 PIVOT,而 SUM(CASE...) 是 ANSI 兼容的通用写法,本质是“按条件把行转为列值再聚合”。它不依赖数据库方言,也不需要额外子查询或 CTE 就能完成单层分组透视。
基本写法:用 CASE 构造虚拟列再 SUM
核心逻辑是:对每个目标分类字段(如 status),写一个 CASE 表达式,匹配时返回 1(或实际数值),否则返回 0;外层用 SUM() 汇总——相当于计数;若要汇总金额,则返回对应金额字段。
-
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END)→ 统计已支付订单数 -
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END)→ 统计已支付总金额 - 多个
CASE并列写在SELECT中,即可生成多列结果
常见错误:NULL 处理和聚合层级错位
最容易出问题的是没处理 NULL 或漏写 ELSE 0,导致整列结果为 NULL;另一个坑是忘记在 GROUP BY 中包含所有非聚合字段——否则 MySQL 会报错,PostgreSQL 直接拒绝执行。
- 不写
ELSE 0:当无匹配行时,CASE返回NULL,SUM(NULL)结果仍是NULL,不是 0 -
GROUP BY必须包含所有未被聚合的SELECT字段,例如按region透视,就得写GROUP BY region - 若需按时间+地区双维度透视,
GROUP BY year, region缺一不可
性能注意:避免在 CASE 中调用函数或子查询
CASE 内部应尽量只做等值判断或简单表达式。如果写成 CASE WHEN UPPER(status) = 'PAID' THEN ...,会导致索引失效;更糟的是嵌套 (SELECT ...),会让每行都触发一次子查询,数据量稍大就卡死。
- 优先用
status IN ('paid', 'shipped')替代多个WHEN分支 - 确保被判断字段(如
status)有索引,且类型一致(避免隐式转换) - MySQL 8.0+ 可考虑用
JSON_OBJECTAGG配合GROUP_CONCAT做动态列,但SUM(CASE...)仍是静态列场景下最稳的选择











