COUNT(CASE WHEN) 更快因只需一次全表扫描,而多次 COUNT(*) 会触发多次扫描;其标准写法为 COUNT(CASE WHEN condition THEN 1 END),自动忽略 NULL,误写 ELSE 0 会导致结果偏高。

为什么 COUNT(CASE WHEN) 比多次 COUNT(*) + WHERE 更快
因为数据库引擎只需对表做一次全扫描,所有条件在同一个聚合阶段完成计算;而多个独立 COUNT(*) 会触发多次全表扫描(除非有覆盖索引且优化器足够聪明,但不可靠)。尤其在大表或分布式查询中,I/O 和网络开销差异明显。
常见错误是写成 COUNT(CASE WHEN condition THEN 1 END) 却漏掉 ELSE NULL —— 其实不写 ELSE 默认就是 NULL,COUNT() 本来就会跳过 NULL,所以没问题;但若误写成 COUNT(CASE WHEN condition THEN 1 ELSE 0 END),就会把 0 当作有效值计入,结果偏高。
-
COUNT()只统计非NULL值,不是“统计所有行” - 用
SUM(CASE WHEN ... THEN 1 ELSE 0 END)更直觉,语义更清晰,性能几乎无差别 - 避免在
CASE中返回字符串或复杂表达式——只返回1或NULL最安全
COUNT(CASE WHEN) 的标准写法与典型场景
适用于按维度分组后,同时统计多个业务口径指标,比如:订单数、支付成功数、退款数、高客单价订单数。关键在于所有 CASE 共享同一行上下文,无需子查询或 JOIN。
示例:统计各城市「总订单」、「已支付订单」、「金额 ≥ 500 的订单」
SELECT city, COUNT(*) AS total_orders, COUNT(CASE WHEN status = 'paid' THEN 1 END) AS paid_orders, COUNT(CASE WHEN amount >= 500 THEN 1 END) AS high_value_orders FROM orders GROUP BY city;
- 所有
CASE必须放在同一SELECT的聚合层,不能嵌套在子查询里再套COUNT - 如果需要去重计数(如不同用户数),改用
COUNT(DISTINCT CASE WHEN ... THEN user_id END),注意部分数据库(如 MySQL 5.7)不支持DISTINCT和CASE混用,此时需用SUM+ 子查询绕过 - PostgreSQL 支持
FILTER语法(如COUNT(*) FILTER (WHERE status = 'paid')),语义更干净,但 MySQL、SQL Server 不支持
容易被忽略的 NULL 处理和性能陷阱
当 CASE 条件涉及可能为 NULL 的字段(如 user_id、coupon_code),直接写 WHEN coupon_code = 'DISC20' 会漏掉 coupon_code IS NULL 的行——这不是 bug,是 SQL 三值逻辑的正常行为,但常被当成统计不准。
- 显式处理
NULL:用CASE WHEN coupon_code = 'DISC20' OR coupon_code IS NOT NULL AND ...太冗长,推荐拆逻辑或补默认值 - 在
WHERE中提前过滤掉无关行(如WHERE status IN ('paid', 'refunded'))能显著减少扫描量,但要注意这会影响分母(比如想算“退款率”,分母应是全部订单,就不能加WHERE status = ...) - 如果条件复杂(如多字段组合判断),先用
COALESCE或NULLIF标准化输入,再进CASE,避免重复判空
和 SUM(CASE WHEN) 对比:选哪个?
功能上完全等价:COUNT(CASE WHEN cond THEN 1 END) ≡ SUM(CASE WHEN cond THEN 1 ELSE 0 END)。区别在于语义和 NULL 安全性。
- 用
COUNT:强调“计数”,但要求你记住它自动忽略NULL,否则易错(比如误写ELSE 'N'导致类型转换失败) - 用
SUM:逻辑更线性,“满足就加 1,否则加 0”,不易出错,也方便扩展为加权重(如THEN amount统计总金额) - 某些旧版 Hive 或 Spark SQL 对
COUNT(CASE)优化不佳,实际执行计划仍拆成多次扫描,此时强制用SUM更稳
真正复杂的地方不在语法,而在厘清每个指标的业务定义是否共享同一数据快照——比如“当日下单用户数”和“当月累计活跃用户数”不能硬塞进一个 COUNT(CASE),得用窗口函数或预聚合配合。

















