应使用 CASE WHEN 套在聚合函数中实现条件统计,因其只需单次扫描、避免多次查询的网络开销与数据不一致,支持一行多桶计算,并能同句返回总览与分项结果。

直接回答:用 CASE WHEN 套在聚合函数里,而不是写多个 WHERE 子句或拆成多条查询。
为什么不能用 WHERE 分多次查
有人会想“查待审核数量”就 SELECT COUNT(*) FROM t WHERE status = 'pending',“查已通过数量”再跑一条 SELECT COUNT(*) FROM t WHERE status = 'approved'……这样查 5 个状态就得发 5 次请求,网络开销翻倍,还容易因中间数据变更导致统计口径不一致。
更关键的是,WHERE 是在分组前过滤整行,没法让一行同时参与多个统计维度 —— 而条件聚合的本质,是让同一行根据不同逻辑“算进不同桶里”。
- 每条记录只扫描一次,性能更好
- 结果在同一行返回,便于比对(比如“通过率 = approved / total”)
- 避免并发更新导致的统计漂移
CASE WHEN + COUNT/SUM 的标准写法
核心模式就这一种:COUNT(CASE WHEN condition THEN 1 END) 或 SUM(CASE WHEN condition THEN 1 ELSE 0 END)。两者语义等价,但 COUNT 写法更常见,因为 COUNT 对 NULL 不计数,省掉 ELSE 0。
示例:统计订单表中各状态数量
SELECT
COUNT(*) AS total,
COUNT(CASE WHEN status = 'created' THEN 1 END) AS created_cnt,
COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_cnt,
COUNT(CASE WHEN status IN ('shipped', 'delivered') THEN 1 END) AS shipped_cnt,
COUNT(CASE WHEN status = 'cancelled' THEN 1 END) AS cancelled_cnt
FROM orders;
注意点:
-
CASE WHEN后面不要跟ELSE—— 留空即为NULL,COUNT自动跳过 - 用
IN合并多个值比写多个OR更清晰、也更易读 - 字段名别用引号包裹(除非含特殊字符),MySQL/PostgreSQL/SQL Server 都支持裸名
HAVING 不能替代条件聚合
有人误以为 HAVING 能筛出“某状态数量 > 100”的组,就等于做了条件统计 —— 不对。HAVING 是对 GROUP BY 后的分组结果做筛选,它本身不生成新列。
比如下面这段代码:
SELECT status, COUNT(*) FROM orders GROUP BY status HAVING COUNT(*) > 100;
它只返回那些“数量超 100”的状态及其总数,但不会告诉你总共有多少订单、其他状态有多少——你依然缺一个全局基准值。
真正需要的是:同一查询里既有分组明细,又有跨状态的条件汇总(比如“已支付订单占总量的百分比”),这时候必须靠 CASE WHEN 在 SELECT 里展开计算。
兼容性与性能提醒
所有主流数据库(MySQL 5.7+、PostgreSQL、SQL Server、Oracle、SQLite 3.8+)都支持这种写法,语法完全一致,无需改写。
唯一容易踩的坑是 NULL 处理:
- 如果
status字段允许为NULL,且你想单独统计NULL数量,得显式写COUNT(CASE WHEN status IS NULL THEN 1 END) - 别用
= NULL判断,必须用IS NULL - 聚合函数里混用
NULL和数字时,确保逻辑无歧义(比如SUM遇到NULL直接忽略,不影响结果)
实际执行时,优化器通常能把这种写法压成单次全表扫描,和最朴素的 SELECT COUNT(*) 开销接近——只要没在 CASE 里调用复杂函数或子查询。

















