如何用SUM(CASE WHEN...)在SQL中根据布尔条件计数?
- 内容介绍
- 相关推荐
本文共计741个文字,预计阅读时间需要3分钟。
在SQL查询中,使用COUNT函数统计非NULL值时,直接使用COUNT(*)会统计所有行,包括NULL值。若要仅统计满足特定条件的行数,可以使用COUNT表达式。
例如,统计状态为'active'的记录数,可以使用以下SQL语句:
SUM(CASE WHEN ...) 是最通用的跨数据库解法
核心思路:把满足条件的行转成 1,不满足的转成 0,再用 SUM() 累加。这比 COUNT() 更可控,且所有主流数据库都支持。
- 写法固定:
SUM(CASE WHEN condition THEN 1 ELSE 0 END) - 避免漏掉
ELSE 0:如果省略,不满足条件的行会变成 NULL,SUM()会跳过它们(结果偏小) - 别用
COUNT(CASE WHEN ... THEN 1 END):它只统计“有 1 的行”,等价于COUNT(*)减去不满足的行数,但语义绕、易误解 - 示例:统计已支付订单数
SUM(CASE WHEN paid_at IS NOT NULL THEN 1 ELSE 0 END)
替代方案对比:COUNT + NULL 处理 vs. SUM + CASE
有人用 COUNT(CASE WHEN condition THEN 1 END),它依赖隐式忽略 NULL 的行为。虽然结果常对,但逻辑是“数出满足条件的行”,不是“算满足条件的个数”——当字段本身可能为 NULL 时容易混淆。比如:
-
COUNT(CASE WHEN score > 80 THEN score END):如果score是 NULL,即使条件为 FALSE,也不会进分支;但如果score非 NULL 但 ≤80,也不进分支 —— 两者都消失,无法区分原因 -
SUM(CASE WHEN score > 80 THEN 1 ELSE 0 END):明确覆盖所有行,无歧义 - 性能上无实质差异,优化器通常能等价处理这两种写法
注意布尔字段在不同数据库里的类型陷阱
有些数据库(如 PostgreSQL)有原生 BOOLEAN 类型,但 SUM(boolean_column) 会报错(类型不匹配);MySQL 把布尔当作 TINYINT(1),所以 SUM(active) 能工作,但这是 MySQL 特性,不可移植。
- 安全做法永远走
CASE WHEN显式转换 - 别依赖
AVG(condition):虽然数学上等价(平均值 × 行数 = 和),但可读性差,且遇到空集时 AVG 返回 NULL,SUM 返回 0,行为不一致 - 如果用在窗口函数里,同样适用:
SUM(CASE WHEN type = 'error' THEN 1 ELSE 0 END) OVER (PARTITION BY service)
ELSE 0 分支——少写它不会报错,但结果会悄悄变小,尤其在数据分布不均或条件覆盖率低时更难察觉。本文共计741个文字,预计阅读时间需要3分钟。
在SQL查询中,使用COUNT函数统计非NULL值时,直接使用COUNT(*)会统计所有行,包括NULL值。若要仅统计满足特定条件的行数,可以使用COUNT表达式。
例如,统计状态为'active'的记录数,可以使用以下SQL语句:
SUM(CASE WHEN ...) 是最通用的跨数据库解法
核心思路:把满足条件的行转成 1,不满足的转成 0,再用 SUM() 累加。这比 COUNT() 更可控,且所有主流数据库都支持。
- 写法固定:
SUM(CASE WHEN condition THEN 1 ELSE 0 END) - 避免漏掉
ELSE 0:如果省略,不满足条件的行会变成 NULL,SUM()会跳过它们(结果偏小) - 别用
COUNT(CASE WHEN ... THEN 1 END):它只统计“有 1 的行”,等价于COUNT(*)减去不满足的行数,但语义绕、易误解 - 示例:统计已支付订单数
SUM(CASE WHEN paid_at IS NOT NULL THEN 1 ELSE 0 END)
替代方案对比:COUNT + NULL 处理 vs. SUM + CASE
有人用 COUNT(CASE WHEN condition THEN 1 END),它依赖隐式忽略 NULL 的行为。虽然结果常对,但逻辑是“数出满足条件的行”,不是“算满足条件的个数”——当字段本身可能为 NULL 时容易混淆。比如:
-
COUNT(CASE WHEN score > 80 THEN score END):如果score是 NULL,即使条件为 FALSE,也不会进分支;但如果score非 NULL 但 ≤80,也不进分支 —— 两者都消失,无法区分原因 -
SUM(CASE WHEN score > 80 THEN 1 ELSE 0 END):明确覆盖所有行,无歧义 - 性能上无实质差异,优化器通常能等价处理这两种写法
注意布尔字段在不同数据库里的类型陷阱
有些数据库(如 PostgreSQL)有原生 BOOLEAN 类型,但 SUM(boolean_column) 会报错(类型不匹配);MySQL 把布尔当作 TINYINT(1),所以 SUM(active) 能工作,但这是 MySQL 特性,不可移植。
- 安全做法永远走
CASE WHEN显式转换 - 别依赖
AVG(condition):虽然数学上等价(平均值 × 行数 = 和),但可读性差,且遇到空集时 AVG 返回 NULL,SUM 返回 0,行为不一致 - 如果用在窗口函数里,同样适用:
SUM(CASE WHEN type = 'error' THEN 1 ELSE 0 END) OVER (PARTITION BY service)
ELSE 0 分支——少写它不会报错,但结果会悄悄变小,尤其在数据分布不均或条件覆盖率低时更难察觉。
