正确条件求和应使用SUM(CASE WHEN ... THEN 1 ELSE 0 END),而非SUM(status='paid')或COUNT(CASE);需显式ELSE 0防NULL,多状态需多个独立SUM,复合条件直接在WHEN中用AND组合。

用 CASE WHEN 套在 SUM() 里才能做条件求和
直接写 SUM(status = 'paid') 在 MySQL 里看似能跑,但本质是把布尔值转成 0/1 再加总,可读性差、跨数据库不兼容,且无法处理多分支逻辑。真正可控的方式是把 CASE WHEN 当作 SUM() 的输入表达式——它必须返回数值(通常是 0 或 1,也可以是金额、数量等),SUM() 对这一列结果累加。
常见错误是把 CASE 写在 SUM 外面,比如:SUM(CASE WHEN ... THEN 1 END) ✅ 正确;CASE WHEN ... THEN SUM(...) END ❌ 这会按分组逻辑错乱,甚至报错“无效的 GROUP BY 引用”。
- 每个
CASE分支的THEN必须返回同类型数值,不能混用字符串和数字 -
ELSE 0强烈建议显式写出,避免NULL被SUM()忽略导致统计值偏小 - MySQL 8.0+ 和 PostgreSQL 支持
SUM() FILTER (WHERE ...)语法,但兼容性不如CASE WHEN
统计多个互斥状态时,每个 CASE WHEN 独立套一层 SUM()
比如要同时统计「已支付」「已取消」「待审核」订单数,不能在一个 CASE 里用 WHEN ... THEN 1 ELSE 0 拼成一列——那样只能得到一个总数。必须为每种状态写一个独立的 SUM(CASE WHEN ...) 表达式,它们并列出现在 SELECT 中,共享同一层 GROUP BY。
SELECT COUNT(*) AS total, SUM(CASE WHEN status = 'paid' THEN 1 ELSE 0 END) AS paid_count, SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) AS cancelled_count, SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS pending_count FROM orders GROUP BY DATE(created_at);
- 别用
COUNT(CASE WHEN ...):虽然部分数据库允许,但语义不清,且COUNT(NULL)会被跳过,不如SUM(0/1)直观可靠 - 如果某状态需要加权(如「已支付」按金额统计),把
THEN amount替换THEN 1即可,无需改结构 - WHERE 条件尽量前置到
FROM ... WHERE,而不是全塞进CASE,否则可能影响索引使用
嵌套 CASE WHEN 处理复合条件(比如「近7天已支付且金额>100」)
当条件涉及多个字段或时间范围,直接在 THEN 前叠加 AND 即可,不需要额外嵌套 CASE。例如统计「过去7天内、状态为 paid、金额超过100的订单笔数」:
SUM(CASE WHEN status = 'paid'
AND amount > 100
AND created_at >= CURRENT_DATE - INTERVAL 7 DAY
THEN 1 ELSE 0 END) AS high_value_recent_paid
- 注意时间函数写法因数据库而异:
CURRENT_DATE - INTERVAL 7 DAY(MySQL),CURRENT_DATE - 7(PostgreSQL),DATEADD(day, -7, GETDATE())(SQL Server) - 复合条件中任意一项为
NULL(如amount为空),整个WHEN判断为FALSE,走ELSE分支,所以不必额外IS NOT NULL判断——除非你明确想排除空值 - 条件顺序不影响结果,但把高筛选率的条件(如时间范围)放前面,有助于短路判断,对性能有微弱帮助
遇到 NULL 聚合结果,先查 CASE 是否漏了 ELSE
如果某列 SUM(CASE WHEN ...) 返回 NULL 而不是 0,大概率是所有行都不满足 WHEN 条件,且没写 ELSE,导致整列都是 NULL,SUM(NULL) 结果仍是 NULL。这不是 bug,是 SQL 标准行为。
- 快速验证:把
SUM换成COUNT或MAX看是否同样为NULL,确认是数据问题还是逻辑缺失 - 修复方式只有两个:补
ELSE 0,或在外层套COALESCE(SUM(...), 0) - 更隐蔽的情况是
WHEN中用了可能为NULL的字段参与比较(如status = NULL),这永远不成立,应改为status IS NULL
多条件聚合本身不难,难的是条件边界是否覆盖全、NULL 处理是否一致、以及不同数据库对布尔表达式的隐式转换是否真按你想的在执行。










