在关系型数据库里,行列转换是报表开发里非常常见的需求。比如原始数据是按月份一行一行记的销售金额,业务方却希望看到每个月份作为一列的横表。这种场景用SQL子查询嵌套CASE WHEN是最直观也最通用的做法。

一、准备示例数据
假设有一张销售记录表 sales,结构如下:
| region | month | amount |
|---|---|---|
| 华东 | 1月 | 100 |
| 华东 | 2月 | 150 |
| 华北 | 1月 | 90 |
二、用子查询加嵌套CASE WHEN做行转列
核心思路是先通过子查询把数据按地区和月份聚合,再在外层用CASE WHEN把不同的月份值变成独立列。
SELECT region, SUM(CASE WHEN month = '1月' THEN amount ELSE 0 END) AS jan_amount, SUM(CASE WHEN month = '2月' THEN amount ELSE 0 END) AS feb_amount FROM ( SELECT region, month, amount FROM sales ) t GROUP BY region;
代码说明
- 内层子查询只是把原始表包了一层,实际场景里可以放更复杂的过滤逻辑。
- 外层每个CASE WHEN只在本行月份匹配时取amount,否则取0。
- 用SUM聚合是为了把同一地区多行压成一行,同时把对应月份的金额加起来。
三、多维度嵌套的写法
如果还要按产品类型拆分,可以在CASE WHEN里再嵌套判断:
SELECT region, SUM(CASE WHEN month = '1月' AND ptype = 'A' THEN amount ELSE 0 END) AS jan_a, SUM(CASE WHEN month = '1月' AND ptype = 'B' THEN amount ELSE 0 END) AS jan_b FROM ( SELECT region, month, ptype, amount FROM sales_detail ) t GROUP BY region;
四、常见注意点
避免漏掉聚合函数
CASE WHEN出来的列必须放进聚合函数,否则GROUP BY会报错。
ELSE分支不能省
不写ELSE时数据库会返回NULL,用SUM加起来通常没问题,但用MAX或字符串拼接时就容易出空值。
行列转换不一定非要用PIVOT语法,子查询加CASE WHEN在MySQL、PostgreSQL、SQL Server里都能跑,迁移成本最低。
五、小结
通过子查询先把数据范围框住,再用嵌套CASE WHEN把行里的字段值映射成列,最后配合聚合函数收口,就能稳定实现行列转换。这种写法不依赖数据库特有语法,也方便后期加条件,是日常SQL开发里很实用的套路。