用 count(case when ... then 1 end) 可在单次扫描中精准统计特定状态,因 count 仅计非 null 值,case 不满足条件时默认返回 null;误加 else 0 会导致全量计数,多状态并列统计应同 select 多个 count(case...) 实现。

直接用 COUNT(CASE WHEN ... THEN 1 END) 就行,别写 COUNT(*) 加 WHERE 分四次查——那样既慢又不准,还容易因并发写入导致统计口径错位。
为什么 COUNT(CASE WHEN ... THEN 1 END) 能统计特定状态
COUNT() 只数非 NULL 值,而 CASE WHEN 在条件不成立时默认返回 NULL(不写 ELSE 就是这个行为)。所以你只用让满足状态的行返回一个非 NULL 值(比如 1),其余自动被忽略。
- 写
COUNT(CASE WHEN status = 'completed' THEN 1 END)→ 只统计status = 'completed'的行 - 写
COUNT(CASE WHEN status = 'completed' THEN 1 ELSE 0 END)→ 错!0是非NULL值,会把所有行都算进去,结果等于总行数 - 如果字段本身可能为
NULL(比如status允许为空),记得显式判断:CASE WHEN status IS NULL THEN 1 END,不能只写= ''
多个状态并列统计要怎么写
同一 SELECT 里可以并排写多个 COUNT(CASE ...),每个对应一个状态分支,全部在一次扫描中完成。
例如统计订单表中三种状态数量:
SELECT COUNT(CASE WHEN status = 'pending' THEN 1 END) AS pending_count, COUNT(CASE WHEN status = 'completed' THEN 1 END) AS completed_count, COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled_count FROM t_order;
- 不要用
ELSE 0,否则所有分支都会贡献至少一个计数,结果全乱 - 如果状态值有大小写混用(如
'Completed'和'completed'),先统一用LOWER(status)或加校对规则,否则漏统计 - MySQL 8.0+ 支持生成列,若该统计高频使用,可考虑把状态归一化后建虚拟列 + 索引加速
和 GROUP BY 搭配时最容易踩的坑
一旦加上 GROUP BY(比如按日期、地区分组),CASE WHEN 内部不能再引用未分组的非聚合字段,否则 MySQL 严格模式下直接报错。
- 正确:分组字段(如
order_date)和常量可直接用;CASE WHEN order_date > '2026-01-01' THEN 1 END合法 - 错误:
CASE WHEN user_id > 1000 THEN 1 END—— 若user_id不在GROUP BY列表里,且没套聚合函数,就非法 - 空值处理要一致:比如想统计每组里
status为空的比例,分子用COUNT(CASE WHEN status IS NULL THEN 1 END),分母必须是COUNT(*),不能用COUNT(status)(它自动跳过NULL)
最易被忽略的是:当状态字段存在隐式类型转换(比如 status 是字符串但你写了 = 1),MySQL 可能全表扫且不走索引。务必确认字段类型和比较值类型一致,必要时加 CAST 或改用字符串字面量。











