sum必须配合case when实现条件求和,核心是将“是否计入”转化为“返回数值或0”,需显式写else 0防null污染,且所有分支须同类型以避免隐式转换错误。

SUM 本身不支持直接写条件表达式,必须配合 CASE WHEN 或 IIF(SQL Server)来实现条件求和,硬写 SUM(IF(...)) 在 MySQL 8.0.16+ 虽可运行,但语义不清、易出错。
用 CASE WHEN 实现多条件分支求和
这是最通用、跨数据库兼容的做法。核心是把「是否计入求和」转化为「返回数值 or 0」。
常见错误:漏写 ELSE 0 —— 如果某行不满足任何 WHEN 条件,CASE 默认返回 NULL,而 SUM(NULL) 不影响结果,但若后续参与计算(如除法),NULL 会污染整条结果。
使用场景:统计某类订单金额、按状态分组计价、排除测试数据等。
示例(MySQL / PostgreSQL / SQL Server):
SELECT SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS paid_total, SUM(CASE WHEN user_id > 1000 AND created_at > '2024-01-01' THEN amount ELSE 0 END) AS new_vip_revenue FROM orders;
MySQL 中误用 IF 的风险
MySQL 支持 IF(condition, true_value, false_value),看起来更短,但要注意:
-
IF是 MySQL 特有,迁移到 PostgreSQL 或 SQL Server 会直接报错ERROR 1305 (42000): FUNCTION db.IF does not exist - 如果
false_value写成NULL(如IF(status='paid', amount, NULL)),SUM 会跳过该行——这看似合理,但一旦和其他字段做COALESCE或AVG混用,逻辑容易混乱 - 嵌套多层
IF可读性远低于CASE WHEN
推荐写法(明确、安全):
SELECT SUM(IF(status = 'paid', amount, 0)) AS paid_total FROM orders;
WHERE 过滤 vs 条件求和的区别
很多人混淆 SUM(...) WHERE ... 和 SUM(CASE WHEN ...)。关键区别在于:前者是「先过滤再求和」,后者是「全表扫描,按行判断是否计入」。
举个典型坑:
- 想查「每个用户已支付金额 + 待支付金额」,但用
WHERE status IN ('paid','pending')会丢失 status 为cancelled的用户记录(哪怕他们有历史 paid 订单) - 正确做法是用
CASE分别计算各状态,再GROUP BY user_id,保证用户维度不丢失
性能提示:如果条件列(如 status)有索引,且你只需要某一种状态的总和,单独用 WHERE 通常比 CASE 更快;但需要多状态并行统计时,CASE 一次扫描更优。
空值与类型隐式转换的陷阱
SUM 会自动忽略 NULL,但 CASE WHEN 的 ELSE 分支若没写或写了 NULL,会导致该行不参与求和——这有时是故意的,但更多时候是疏忽。
另一个隐形问题:当 THEN 返回字符串(比如误写成 THEN '0'),MySQL 可能隐式转成数字,PostgreSQL 则直接报错 ERROR: CASE types character varying and numeric cannot be matched。
务必确保所有 THEN 和 ELSE 分支返回相同类型,推荐显式写 0.0 或 CAST(0 AS DECIMAL(10,2))。
最容易被忽略的是聚合后除法:比如 SUM(CASE WHEN ... THEN amount ELSE 0 END) / COUNT(*),如果分母是全表行数,而分子只含部分行,结果会被拉低——这时候得确认业务上是否真要「人均全量用户」,还是该用 COUNT(CASE WHEN ... THEN 1 END) 做分母。











