最常用、兼容性最好、语义最清晰的做法是SUM(CASE WHEN ... THEN 1 ELSE 0 END),将条件转为0/1后求和,适用于MySQL 5.7+、PostgreSQL、SQL Server、Oracle等主流数据库。

SQL里怎么用SUM加CASE WHEN算分组内符合条件的行数
直接说结论:这是最常用、兼容性最好、语义也最清晰的做法。比COUNT嵌套IF或子查询更稳妥,尤其在MySQL 5.7、PostgreSQL、SQL Server、Oracle里都稳。
核心逻辑是把“是否满足条件”转成 1 或 0,再用SUM加总——本质是手动实现布尔求和。
常见错误现象:COUNT(CASE WHEN ... THEN 1 END)也能跑通,但容易误写成COUNT(CASE WHEN ... THEN 1 ELSE 0 END),结果全变成COUNT(*)(因为COUNT会忽略NULL,但不会忽略0)。
实操建议:
-
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END)—— 明确补ELSE 0,避免漏掉未匹配行 - 别写
SUM(CASE WHEN ... THEN 1 END),缺ELSE会导致未命中条件的行算作NULL,SUM自动跳过,结果偏小 - 如果字段可能为
NULL,比如status有NULL值,要提前想好是否计入“不满足”,通常得写成CASE WHEN status IS NOT NULL AND status = 'paid' THEN 1 ELSE 0 END
GROUP BY + SUM(CASE WHEN ...) 的典型使用场景
不是所有“条件计数”都要这么写,但以下情况基本绕不开:
比如统计每个用户订单中“已支付”“已取消”“待发货”的数量;或者按日期分组,分别算当天“响应时间 10s”的请求量。
关键点在于:你不是只要一个总数,而是要在同一行里并列展示多个条件的计数值。
实操建议:
- 每个条件单独写一个
SUM(CASE WHEN ...),不要试图塞进一个表达式里 - 字段别名要见名知意,比如
SUM(CASE WHEN type = 'error' THEN 1 ELSE 0 END) AS error_cnt - 如果条件复杂(比如含
OR、多字段组合),先在WHERE里试跑确认逻辑,再挪进CASE——避免CASE里写错优先级
和 COUNT(IF(...))、COUNT(*) FILTER (...) 的区别在哪
MySQL用户常想用COUNT(IF(status='paid', 1, NULL)),PostgreSQL用户倾向COUNT(*) FILTER (WHERE status = 'paid')。它们都能实现,但坑不少。
COUNT(IF(...))的问题是:MySQL 8.0之前不支持窗口函数里用IF做条件聚合;且IF不是标准SQL,换数据库迁移成本高。
FILTER虽简洁,但只在PostgreSQL 9.4+支持,SQLite、SQL Server、旧版MySQL完全不认,一粘贴就报错syntax error near FILTER。
实操建议:
- 团队用多种数据库,或项目要长期维护 → 死守
SUM(CASE WHEN ...),兼容性第一 - 纯PostgreSQL内部工具脚本 → 可用
FILTER,写起来少打字,但别忘了加注释说明依赖版本 - 别混用:
COUNT(CASE WHEN ...)和SUM(CASE WHEN ...)行为不同,前者数非空,后者求和,数值结果一样但语义和可读性差一截
性能和NULL处理的隐性影响
看起来只是语法差异,但执行计划和结果精度真会出问题。
比如SUM(CASE WHEN x > 100 THEN 1 END),当x为NULL时,整个CASE返回NULL,SUM跳过——这符合预期;但如果你写了SUM(CASE WHEN x > 100 THEN 1 ELSE 0 END),NULL被强制转成0,就会把NULL行也算进“不满足”,可能掩盖数据质量问题。
实操建议:
- 查数据质量时,先用
COUNT(*)和COUNT(col)对比,确认目标字段NULL比例,再决定CASE里要不要显式处理NULL - 涉及金额、分数等数值型条件(如
score >= 90),注意score本身为NULL时,比较结果恒为UNKNOWN,必须用IS NULL单独判断 - 大表上跑这类聚合,确保
WHERE能先过滤掉大量无关行(比如加AND created_at >= '2024-01-01'),否则CASE会在每行都执行,没走索引就慢
真正麻烦的从来不是写法本身,而是条件边界没想清——比如“超时”定义是“> 300s”还是“>= 300s”,NULL算不算超时,这些细节一旦定错,SUM再准也没用。










