条件聚合是什么
条件聚合把判断塞进聚合函数内部,用 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,避免报表大面积空值。