SUM(CASE WHEN)是单行多状态计数最稳妥写法,需显式ELSE 0防NULL,横向并列多个SUM可一次扫描完成统计,避免COUNT(CASE)漏计、子查询低效及大小写/空值等数据脏问题。

SQL里用SUM(CASE WHEN)做单行多状态计数,本质是布尔转整型累加
直接结论:SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) 是最稳妥的写法,别用 COUNT(CASE WHEN ...) —— 后者会把 NULL 当作不计数项,而你往往需要“0”这个明确数值。
核心逻辑是把每个条件判断转成 0 或 1,再用 SUM 累加。它不依赖分组,也不要求 GROUP BY,天然适合在聚合查询中并列统计多个状态。
- 必须写
ELSE 0,否则CASE默认返回NULL,SUM(NULL)仍为NULL,不是 0 - 所有
CASE表达式都放在同一层SELECT中,彼此独立互不影响 - 如果某状态字段可能为
NULL,记得在条件里显式处理,比如status IS NULL单独分支
实际写法:一个SELECT里并列多个SUM(CASE WHEN),别嵌套也别拆查询
常见错误是为每种状态写一个子查询或 UNION ALL,既慢又难维护。正确姿势是横向展开:
SELECT SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_count, SUM(CASE WHEN status = 'approved' THEN 1 ELSE 0 END) AS approved_count, SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) AS rejected_count, COUNT(*) AS total_count FROM orders;
这样只扫表一次,执行计划干净,且结果是一行四列,符合“一行中统计不同状态”的需求。
- 列别名(如
pending_count)建议全用小写+下划线,避免大小写敏感问题 - 如果要兼容 MySQL 5.7 之前版本,注意
CASE内不能直接比较ENUM字段和字符串字面量,需先CAST - Oracle 用户注意:
SUM对空集返回NULL,不是 0;如需强制为 0,外层套NVL(..., 0)
遇到NULL状态、空字符串、大小写混杂时怎么防错
真实数据里 status 往往不干净:有空格、大小写不一、空字符串甚至 NULL。硬写 = 'active' 很容易漏数。
- 统一清洗:用
TRIM(UPPER(status)) = 'ACTIVE'处理空格和大小写 - 单独捕获空值:
SUM(CASE WHEN status IS NULL THEN 1 ELSE 0 END) AS null_status_count - 把空字符串视同
NULL:CASE WHEN TRIM(status) = '' OR status IS NULL THEN 1 ELSE 0 END - MySQL 用户慎用
status = 'active' OR status NULL——虽能安全比 NULL,但可读性差,易被误读为赋值
性能和可读性平衡:别为了“简洁”牺牲明确性
有人会把多个 CASE 压缩成一个,比如用 DECODE(Oracle)或 IF(MySQL),但可读性和跨库兼容性立刻下降。
- 坚持用标准
CASE WHEN,所有主流数据库都支持,迁移成本最低 - 别用
COUNT(CASE WHEN ... THEN 1 END)代替SUM(CASE ... ELSE 0 END),语义不同:前者只计非空分支,后者强制归零 - 如果状态种类超过 5–6 种,考虑是否真需要全堆在一行——业务侧可能更想要带状态标签的两列表(
status,cnt),此时用GROUP BY status更自然
真正容易被忽略的是:当后续要加 WHERE 条件过滤整个结果集时,这些 SUM 计算仍基于原始扫描范围,不会自动跟着 WHERE 缩减——除非你把条件写进每个 CASE 的 WHEN 子句里,或者套一层子查询。这点常导致统计口径对不上。










