应优先用sum(case when...)而非count做条件计数,因其能返回0而非跳过null;多指标须共用同一case结构以避免重复扫描;group by字段须与select中非聚合字段严格一致;需警惕源字段null及join后null导致的指标归零。

为什么不用COUNT而要用SUM(CASE WHEN ...)?
因为 COUNT 会把 NULL 当作“不存在”直接跳过,而分类统计时你往往需要“0”这个明确数值——比如某类用户没下单,就得显示 0 而不是漏掉一行。用 SUM(CASE WHEN condition THEN 1 ELSE 0 END) 才能确保每个分组都产出确定值。
常见错误现象:COUNT(CASE WHEN status = 'paid' THEN 1 END) 会把未满足条件的行算作 NULL,最终被 COUNT 忽略,结果偏小。
- 永远优先用
SUM(CASE ...)做条件计数,除非你明确想排除空值 -
COUNT(*)只适合统计总行数,不参与条件分支 - MySQL 中
BOOL表达式可直接进SUM(如SUM(status = 'paid')),但 PostgreSQL/SQL Server 不支持,跨库慎用
多个指标共用一个CASE WHEN还是分开写?
必须共用——即在一个 SUM(CASE WHEN ...) 里嵌套多组逻辑,而不是对同一字段反复 CASE。否则扫描次数翻倍,执行计划里 Seq Scan 或 Index Scan 会重复触发。
使用场景:统计「支付金额」「订单数」「新客数」三个指标,都基于 orders 表且需按 category 分组。
错误写法:SUM(CASE WHEN paid THEN amount END)、COUNT(CASE WHEN paid THEN 1 END)、COUNT(CASE WHEN is_new THEN 1 END) —— 看似清晰,实际让优化器难以合并扫描。
- 正确做法:所有指标统一用
SUM(CASE WHEN ... THEN xxx ELSE 0 END),共享同一遍表扫描 - 注意
ELSE 0必须显式写出,否则默认为NULL,导致SUM结果为NULL - 若某指标需去重(如新客数),改用
COUNT(DISTINCT CASE WHEN ... THEN user_id END),但会轻微拖慢性能
GROUP BY 字段缺失或错位引发的隐性错误
典型错误是只按业务维度(如 category)分组,却在 SELECT 里写了未聚合也未出现在 GROUP BY 中的字段,比如 user_name。MySQL 5.7+ 默认拒绝,PostgreSQL 直接报错 column "xxx" must appear in the GROUP BY clause。
更隐蔽的问题:把时间字段粗粒度分组(如 DATE(created_at))后,又在 CASE WHEN 里用了 created_at > '2024-01-01 12:00' 这种精确到秒的条件——逻辑没错,但容易让人误以为分组粒度也是小时级。
-
GROUP BY字段必须和SELECT中所有非聚合字段完全一致(包括函数包装) - 如果要用不同粒度做条件判断,保持分组字段独立,别混用
DATE()和原生TIMESTAMP - 在 WHERE 中提前过滤(如
WHERE status IN ('paid', 'shipped'))比在CASE里判断更高效
NULL 值穿透导致指标归零的排查点
最常被忽略的是源字段本身含 NULL:比如 amount 为 NULL 时,SUM(CASE WHEN paid THEN amount END) 会跳过整行,而非加 0。结果就是“该分类有 10 笔支付单,但统计金额为 0”。
另一个坑是 JOIN 后未处理右表缺失:LEFT JOIN 用户表后,用 u.is_vip 做 CASE 判断,但没意识到 u.is_vip 可能是 NULL,导致所有未匹配用户被归入“ELSE 0”分支,掩盖了数据关联问题。
- 检查原始字段是否允许
NULL,必要时用COALESCE(amount, 0)预处理 -
CASE WHEN u.is_vip IS TRUE THEN 1 ELSE 0 END比CASE WHEN u.is_vip THEN 1 ELSE 0 END更安全 - 临时加一列
COUNT(*)对照总数,能快速发现指标丢失是否源于 NULL 或 JOIN 失败










