应使用 sum(case when ... then 1 else 0 end) 替代 count(case when ... then 1 end),因后者遇不匹配行返回 null 而 count 会跳过,导致结果缺失 0;sum 方式每行必贡献 0 或 1,结果恒为非空整数,语义清晰、跨库兼容、支持多条件并列统计且适用于 group by 场景。

用 SUM(CASE WHEN ... THEN 1 ELSE 0 END) 替代 COUNT(CASE WHEN ... THEN 1 END)
直接用 COUNT(CASE WHEN status = 'paid' THEN 1 END) 看似简洁,但结果列里没匹配的行会变成 NULL,而 COUNT() 会跳过所有 NULL —— 这意味着它根本不输出“0”,只算“有”的行数。前端取值时拿到 NULL,可能触发崩溃或百分比计算错乱。
正确写法必须显式补 ELSE 0,并用 SUM:
SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END)
这样无论条件是否命中,每行都贡献 0 或 1,聚合后一定是整数,不会丢位。
-
COUNT(CASE ...)本质是“数非 NULL 值”,不是“数满足条件的行” -
SUM(CASE ...)是“把布尔逻辑转成数值再累加”,语义清晰且可控 - 哪怕字段本身含
NULL(如user_id IS NULL),只要在WHEN中覆盖或靠ELSE 0拦住,就不会静默丢失数据
多个并列条件要各自独立写 SUM(CASE ...)
想在同一查询里统计“已支付”“已发货”“已取消”三类数量,别在一个 CASE 里嵌套分支,也别拼 COUNT + OR。每个条件单独一行 SUM(CASE WHEN ... THEN 1 ELSE 0 END),堆在 SELECT 列表里最稳。
例如:
SELECT SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_count, SUM(CASE WHEN status = 'shipped' THEN 1 ELSE 0 END) AS shipped_count, SUM(CASE WHEN region = 'east' AND YEAR(order_date) = 2023 THEN 1 ELSE 0 END) AS east_2023_count FROM orders;
- 组合条件(AND)直接写进
WHEN子句,不用拆开 - OR 条件同理:
CASE WHEN status = 'shipped' OR status = 'delivered' THEN 1 ELSE 0 END - 字符串比较务必用单引号:
'east',双引号在部分数据库(如 PostgreSQL)会被当标识符处理
别用 MySQL 的 IF() 函数替代 CASE WHEN
COUNT(IF(status='active', 1, NULL)) 看起来短,但隐患大:一旦写成 COUNT(IF(status='active', 1, 0)),0 是非 NULL 值,COUNT() 就会把所有行都算进去——结果翻倍还查不出错。
更关键的是:IF() 是 MySQL 特有函数,在 PostgreSQL、SQL Server、Oracle 里根本不存在。换库就得重写。
- 统一用
SUM(CASE WHEN ... THEN 1 ELSE 0 END),跨库兼容、语义直白、分支易扩展 - 团队协作时,没人需要猜你
IF里漏没漏ELSE分支 - 即使当前只跑 MySQL,也建议从第一天就避开
IF()—— 后续迁移成本远高于多敲几个字符
GROUP BY 场景下仍沿用同一模式
分组统计时,别以为 COUNT() 能自动适配多条件。比如按部门统计“在职人数”和“试用期人数”,依然得各自写 SUM(CASE ...),而不是靠 COUNT(*) + WHERE 拆多条查询。
错误做法(性能差、难维护):
SELECT department, (SELECT COUNT(*) FROM employees e2 WHERE e2.department = e1.department AND status = 'onboard') AS onboard_cnt, (SELECT COUNT(*) FROM employees e2 WHERE e2.department = e1.department AND status = 'probation') AS probation_cnt FROM employees e1 GROUP BY department;
正确写法(单次扫描,可读性强):
SELECT department, SUM(CASE WHEN status = 'onboard' THEN 1 ELSE 0 END) AS onboard_cnt, SUM(CASE WHEN status = 'probation' THEN 1 ELSE 0 END) AS probation_cnt FROM employees GROUP BY department;
- 避免子查询导致多次全表扫描,尤其数据量大时性能断崖下跌
-
GROUP BY后的SUM(CASE ...)仍是标准写法,和非分组场景逻辑一致 - 注意:WHERE 过滤应放在 GROUP BY 之前,否则可能漏掉某些分组的零值情况
ELSE 0 的强制存在 —— 它不是可选项,是保证结果可预期的底线。少写一个 ELSE,可能让报表里某列突然变空,而排查时发现日志里只有 NULL,没有报错。










