首页/数据/SQL 速查/条件聚合(多种口径一次算出)
聚合与窗口函数54 条

条件聚合(多种口径一次算出)

用 CASE/布尔表达式在聚合内部区分条件,一条 SQL 同时算出多个指标,避免多次扫描同一张表。

SQL 速查数据更新于 2026-10-13

条件聚合是什么

条件聚合把判断塞进聚合函数内部,用 CASE 区分不同口径,一次扫描同时算出多个指标。相比为每个口径单独写一条 SQL 再拼结果,它只读一遍表,是日报、漏斗这类多指标报表最常用的写法。

基本信息

核心写法SUM(CASE WHEN 条件 THEN 1 ELSE 0 END) 做条件计数
带金额SUM(CASE WHEN 条件 THEN amount ELSE 0 END) 做条件合计
MySQL 简写布尔表达式可直接求和:SUM(status = 1),跨库脚本请用 CASE
条件互斥多个口径要么互斥,要么在注释里写清重叠部分
NULL 处理ELSE 0 必须写,否则未命中行为 NULL 会把整组结果变成 NULL
结果过滤对聚合结果再过滤用 HAVING,对原始行过滤仍用 WHERE

一条 SQL 出四组数

一条 SQL 出四组数
SELECT DATE(created_at) AS day,
       COUNT(*)                                        AS total,      -- 全部订单
       SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END)     AS paid,       -- 已支付笔数
       SUM(CASE WHEN status = 2 THEN 1 ELSE 0 END)     AS refunded,   -- 已退款笔数
       SUM(CASE WHEN status = 1 THEN amount ELSE 0 END) AS paid_amt   -- 已支付金额
FROM orders
WHERE created_at >= '2024-01-01'
  AND created_at <  '2024-02-01'
GROUP BY DATE(created_at)
ORDER BY day;   -- 一次扫描得到四个指标

算率值要防除零

算率值要防除零
SELECT DATE(created_at) AS day,
       SUM(CASE WHEN status = 1 THEN amount ELSE 0 END)
         / NULLIF(SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END), 0) AS avg_price
FROM orders
GROUP BY DATE(created_at);   -- 当天没有支付订单时返回 NULL,而不是报错

常见口径写法

指标写法说明
条件计数SUM(CASE WHEN c THEN 1 ELSE 0 END)等于命中条件的行数
条件合计SUM(CASE WHEN c THEN amt ELSE 0 END)未命中贡献 0,不污染合计
条件去重计数COUNT(DISTINCT CASE WHEN c THEN uid END)CASE 省 ELSE 时未命中为 NULL,被 COUNT 忽略
率值分子 / NULLIF(分母, 0)分母为 0 时返回 NULL 而非报错
口径自检各口径之和与 COUNT(*) 比对不相等说明条件重叠或有漏项

典型使用场景

  • 日报月报:一行同时给出总量、成功量、失败量与金额
  • 漏斗分析:把各环节命中数排在同一行算转化率
  • 分渠道统计通过、拒绝、人工审核的数量
  • 对账场景:一次算出应结、已结、差额三列
  • 替代多条 SQL 拼结果,减少对同一张大表的重复扫描
  • 给同一指标按不同维度切片,配 GROUP BY 多列一次出多组数
COUNT(DISTINCT CASE WHEN 条件 THEN 列 END) 是条件去重计数的标准写法:CASE 不写 ELSE 时未命中返回 NULL,而 COUNT 会忽略 NULL,所以只统计命中行的去重值。
CASE 不写 ELSE 时默认返回 NULL:SUM 一旦遇到 NULL,整组结果就是 NULL,计数类指标务必显式写 ELSE 0,避免报表大面积空值。

易错点

  • SUM 里漏写 ELSE 0:未命中行返回 NULL,整组 SUM 变成 NULL,报表出现成片空值
  • 条件之间互相重叠:多个口径相加大于总数,报表对不上,动手前先划清条件边界
  • 把 MySQL 的 SUM(status = 1) 直接搬到 PostgreSQL:布尔值不能求和,必须改回 CASE 写法
  • CASE 条件与外层 WHERE 重复收窄:口径被过滤两次,指标含义与注释不符
  • 该用 WHERE 时用了 CASE:需要先过滤掉大部分数据时,先 WHERE 缩小行数再条件聚合,扫描量差别很大

数据来源与参考

  • MySQL 8.0 Reference Manual · CASE
  • PostgreSQL Documentation · Aggregate Expressions
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌