← 中文资源

简体中文版本

CASE 聚合行转列透视

用条件聚合实现行转列:FILTER 子句按月份求和形成透视列,CASE WHEN 经典写法对照,动态列清单供应用层拼装。

阅读英文原文

示例说明

SQL 没有内建的 PIVOT 语法,行转列的标准答案是条件聚合:每个输出列对应一个条件分支,聚合时只有匹配行的值参与求和。FILTER 子句是比 CASE WHEN 更清爽的写法——条件写在聚合外面,ELSE 零值的填充语义由 COALESCE 在外层按需补。列的集合是静态的,动态范围要靠应用层拼接。

技术要点

  • FILTER 子句的条件聚合最简洁
  • 每列一个条件分支的透视范式
  • DISTINCT 月份供动态列清单

代码解读

第一种写法三个月份列加总计列,FILTER 的 NULL 语义让 SUM 自动跳过不匹配行——不需要 ELSE 零;第二种 CASE 写法的行为等价,兼容不支持 FILTER 的引擎。末尾查询输出区间内的月份清单,应用层遍历它生成列定义再拼最终 SQL。

使用建议

列数随时间无限增长时改按年拆表或转 JSON 输出。FILTER 与 CASE 的空值语义差异在 AVG 上更明显。动态拼 SQL 必须白名单化月份值。

代码示例

-- 固定月份的手写透视:CASE 进聚合函数
SELECT
  category,
  SUM(amount) FILTER (WHERE month = '2026-07') AS jul_revenue,
  SUM(amount) FILTER (WHERE month = '2026-08') AS aug_revenue,
  SUM(amount) FILTER (WHERE month = '2026-09') AS sep_revenue,
  SUM(amount) AS total_revenue
FROM monthly_revenue
GROUP BY category
ORDER BY total_revenue DESC;

-- 经典写法对照:CASE WHEN 的条件聚合
SELECT
  category,
  SUM(CASE WHEN month = '2026-07' THEN amount ELSE 0 END) AS jul_revenue,
  SUM(CASE WHEN month = '2026-08' THEN amount ELSE 0 END) AS aug_revenue
FROM monthly_revenue
GROUP BY category;

-- 生成动态列清单(应用层据此拼 SQL)
SELECT DISTINCT month
FROM monthly_revenue
WHERE month BETWEEN '2026-07' AND '2026-09'
ORDER BY month;