sum(case when ...) 更适合布尔频次统计,因其显式用else 0将不满足条件行归零,确保每行必贡献0或1,避免count忽略null导致漏计,语义清晰且结果可控。

为什么 SUM(CASE WHEN ...) 比 COUNT(CASE WHEN ...) 更适合布尔频次统计
因为 COUNT() 会把 NULL 和 FALSE 都忽略,而布尔条件中你往往需要区分“不满足”和“未参与”——SUM(CASE WHEN condition THEN 1 ELSE 0 END) 显式归零,结果更可控、语义更清晰。尤其在多条件并列统计时,避免漏计。
常见错误现象:COUNT(CASE WHEN status = 'active' THEN 1 END) 在 status 为 NULL 或 'inactive' 时不计入,但你也无法得知这部分有多少条;而用 SUM 可以配合 ELSE 0 确保每行必贡献 0 或 1。
-
SUM返回整型,天然适配计数场景,无需类型转换 - 当条件嵌套或含运算(如
score > threshold * 1.2)时,ELSE 0能防止意外NULL扰乱求和 - 与
AVG(CASE ...)组合使用时,分母一致(总行数),逻辑更自洽
写法模板与易错参数位置
标准结构是:SUM(CASE WHEN <condition> THEN 1 ELSE 0 END)</condition>。最容易出错的是 ELSE 缺失或写成 ELSE NULL——这会让整列变成 NULL 安全但求和失效。
使用场景举例:按用户地区分组,统计各地区付费用户数、试用期用户数、近7天登录用户数。
SELECT region, SUM(CASE WHEN is_paid = true THEN 1 ELSE 0 END) AS paid_count, SUM(CASE WHEN trial_end_at > NOW() THEN 1 ELSE 0 END) AS trial_count, SUM(CASE WHEN last_login_at >= NOW() - INTERVAL '7 days' THEN 1 ELSE 0 END) AS active7d_count FROM users GROUP BY region;
- PostgreSQL 中时间减法用
INTERVAL,MySQL 用DATE_SUB(NOW(), INTERVAL 7 DAY) - SQL Server 需写
ELSE 0,不能省略(否则默认为NULL) - 条件中避免直接比较布尔字段与字符串(如
is_paid = 'true'),应统一用= true或IS TRUE(依方言而定)
和 WHERE 过滤的区别:什么时候该用分组内条件,什么时候该过滤
关键看是否要保留“零值组”。比如想看所有部门的离职率,包括当前无人离职的部门——必须用 SUM(CASE WHEN left_at IS NOT NULL THEN 1 ELSE 0 END) 放在 SELECT 中;若先用 WHERE left_at IS NOT NULL,那些没离职记录的部门就直接从结果里消失了。
- 用
WHERE:只关心满足某前提的子集(如“只统计已激活用户的行为”) - 用
CASE WHEN+SUM:需在完整分组维度上做条件计数(如“每个城市的新客转化率 = 下单人数 / 注册人数”) - 性能影响:带
WHERE可提前减少扫描行数;CASE在聚合阶段计算,对大表压力略高,但通常可接受
兼容性与方言差异提醒
绝大多数主流 SQL 引擎(PostgreSQL, MySQL 5.7+, SQL Server 2012+, DuckDB, ClickHouse)都支持该写法,但细节有差异:
- SQLite 不支持
BOOLEAN类型,常用INTEGER存 0/1,此时条件写is_deleted = 1即可 - BigQuery 中
BOOL字段可直接用于WHEN,但推荐显式写WHEN is_valid = TRUE提高可读性 - Oracle 无原生布尔类型,常以
CHAR(1)存'Y'/'N',需写WHEN flag = 'Y' THEN 1 ELSE 0 - 别名不能在
GROUP BY中直接引用(如不能写GROUP BY paid_count),必须重复表达式或用列序号
最常被忽略的一点:当 CASE 表达式里混用不同精度数值(如 THEN 1.0 ELSE 0),某些引擎(如 Presto)会升格为浮点,导致后续除法出现小数位——统一用整型 1 和 0 最稳妥。










