最通用、最可控的条件聚合写法是将case when嵌入sum/count等聚合函数内部:sum(case when cond then val else 0 end)用于求和,count(case when cond then 1 end)用于计数(自动忽略null),avg(case when cond then val end)用于均值(避免else 0扭曲结果),所有case必须有end,且需配合group by正确区分行级过滤与组内统计。

直接用 CASE WHEN 嵌在聚合函数里,是最通用、最可控的条件聚合写法。其他方案(比如 PostgreSQL 的 FILTER)虽简洁,但不跨数据库,且容易踩执行顺序或 NULL 处理的坑。
怎么写 SUM/COUNT 的条件聚合
核心是把判断逻辑塞进聚合函数内部,让数据库在扫描时就完成分流计算。
-
SUM(CASE WHEN condition THEN value ELSE 0 END):适合求和类,ELSE 0防止干扰总和 -
COUNT(CASE WHEN condition THEN 1 END):适合计数类,ELSE省略即可,COUNT自动跳过NULL -
AVG(CASE WHEN condition THEN value END):注意别写ELSE 0,否则会把 0 当有效值拉低均值 - 所有
CASE必须有END,漏写会导致语法错误
GROUP BY 场景下常见错误
条件聚合常和 GROUP BY 一起用,但容易混淆“过滤行”和“筛选聚合项”的区别。
- 用
WHERE是先筛数据,影响整个分组;用CASE WHEN是在每组内做条件统计,不影响分组结构 - 想查“每个地区里已完成订单数”,不能写
WHERE status = 'Completed'再COUNT(*),那会丢掉该地区其他状态的记录,导致分组维度丢失 - 正确写法是保留所有行,只在
COUNT(CASE WHEN status = 'Completed' THEN 1 END)里控制统计目标 -
SELECT列表里出现的非聚合字段,必须出现在GROUP BY中,否则报错
PostgreSQL 的 FILTER 子句要不要用
可以,但得清楚它和 CASE WHEN 的行为差异,尤其在空结果和类型处理上。
-
COUNT(*) FILTER (WHERE condition)返回0(空集),而SUM(x) FILTER (WHERE false)返回NULL -
FILTER不能引用SELECT中定义的别名,比如SELECT amount AS a FROM t GROUP BY amount HAVING SUM(a) FILTER (WHERE a > 0)会报错 -
FILTER不支持窗口函数,如ROW_NUMBER() FILTER (WHERE ...)是非法语法 - 如果项目要兼容 MySQL 或 SQL Server,就别用
FILTER,老老实实用CASE WHEN
百分比计算怎么避免除零和 NULL
直接除 COUNT(*) 很危险,空分组或全不满足条件时,分母为 0 或分子为 NULL 会导致结果异常。
- 用
NULLIF(denominator, 0)替代硬除,比如SUM(CASE WHEN done THEN 1.0 ELSE 0 END) / NULLIF(COUNT(*), 0) - 需要默认值时,套一层
COALESCE(..., 0),例如COALESCE(AVG(CASE WHEN paid THEN amount END), 0) - 百分比场景建议统一用
1.0或100.0作系数,避免整数除法截断(尤其在 PostgreSQL 和 SQL Server 中) -
AVG本身会忽略NULL,但CASE里返回NULL是安全的;返回0反而会扭曲均值
真正难的不是写对一条语句,而是理解每层过滤(WHERE、GROUP BY、CASE、HAVING)作用在哪一阶段——稍一错位,统计口径就偏了。











