直接用case when行转列易漏数据,因其不聚合不补行,缺失组合不会生成null行;必须配合group by和sum等聚合函数,并用else 0避免null传播,必要时需先构造完整维度网格再left join。

为什么直接用 CASE WHEN 做行转列容易漏数据
因为 CASE WHEN 本身不聚合、不补行,它只是逐行计算新字段值。如果原始数据里某类 category 在某个 month 下完全缺失,CASE WHEN 就不会凭空生成一列为 NULL 的结果——除非你配合 GROUP BY 和聚合函数(如 SUM、COUNT),否则统计口径会错。
常见错误现象:CASE WHEN category = 'A' THEN amount END 返回大量 NULL,没做 SUM() 就直接 SELECT,导致“一列多个值”而非“一行一个汇总值”。
- 必须搭配
GROUP BY(通常是分组维度,如region、year) - 每个
CASE WHEN分支外层必须套聚合函数,如SUM(CASE WHEN ...) - 若需补全缺失组合(如某地区某月无销售),得先构造完整维度组合再
LEFT JOIN,CASE WHEN本身做不到
标准写法:SUM(CASE WHEN ...) + GROUP BY
这是最常用、兼容性最好的动态行转列方式,适用于 MySQL 5.7+、PostgreSQL、SQL Server、Oracle 等主流数据库。
假设表 sales 有字段:region、product、amount,想按地区横向列出各产品销售额:
SELECT region, SUM(CASE WHEN product = 'iPhone' THEN amount ELSE 0 END) AS iphone_sales, SUM(CASE WHEN product = 'MacBook' THEN amount ELSE 0 END) AS macbook_sales, SUM(CASE WHEN product = 'AirPods' THEN amount ELSE 0 END) AS airpods_sales FROM sales GROUP BY region;
注意点:
-
ELSE 0比ELSE NULL更安全,避免聚合后整列为NULL - 所有
CASE WHEN必须出现在同一层级的SELECT中,不能嵌套在子查询里再聚合(除非有意图) - 列名(如
iphone_sales)是静态写的,不是“动态”的——真要动态生成列名,得靠应用层拼 SQL 或数据库特定函数(如 PostgreSQL 的crosstab)
MySQL 8.0+ / PostgreSQL 中用窗口函数辅助补行
当需要强制展示“某地区所有产品”,哪怕某产品销量为 0,仅靠 CASE WHEN 不够,得先生成完整笛卡尔积。
典型做法:用 SELECT DISTINCT product FROM sales 和 SELECT DISTINCT region FROM sales 构造基础网格,再 LEFT JOIN 原表:
WITH regions AS (SELECT DISTINCT region FROM sales),
products AS (SELECT DISTINCT product FROM sales),
grid AS (SELECT r.region, p.product FROM regions r CROSS JOIN products p)
SELECT
g.region,
SUM(CASE WHEN s.product = g.product THEN s.amount ELSE 0 END) AS total
FROM grid g
LEFT JOIN sales s ON g.region = s.region AND g.product = s.product
GROUP BY g.region, g.product;
这个结构才能保证每地每品都有一行,再套 CASE WHEN 才真正可控。
- 别省略
WITH中的DISTINCT,重复值会导致笛卡尔积爆炸 - MySQL 5.7 不支持 CTE,得改用派生表(
FROM (SELECT ...)) - 如果产品种类多(>100),这种写法性能会明显下降,需加索引或预聚合
容易被忽略的 NULL 处理和类型一致性
CASE WHEN 各分支返回值类型不一致时,数据库会隐式转换,可能引发截断、精度丢失或报错(如 PostgreSQL 严格模式下)。
例如:CASE WHEN flag THEN 1 ELSE 'N/A' END 在多数数据库中会把 1 转成字符串,导致无法参与后续数值计算。
- 所有
THEN和ELSE分支尽量保持同类型;数值场景统一用0,文本场景统一用'' - 聚合前用
COALESCE(s.amount, 0)显式处理源数据中的NULL,比依赖ELSE更可靠 - 若列值含小数,确认聚合函数是否用
SUM()(保留精度)而非COUNT()(只计数)
动态行转列真正的复杂点不在 CASE WHEN 语法本身,而在于维度完整性、空值传播路径、以及聚合粒度是否与业务口径对齐——这些地方一错,报表数字就 quietly 错了。










