SUM() 忽略 NULL 是因 NULL 被彻底排除在聚合输入集外,而非跳过或转为 0;仅当结果集为空或所有值均为 NULL 时才返回 NULL;COALESCE(SUM(col), 0) 与 SUM(COALESCE(col, 0)) 语义不同,前者整组兜底,后者逐行补零。

SUM() 忽略 NULL 不是因为“它选择不返回 NULL”,而是因为它根本不会把 NULL 当作一个可参与运算的值——NULL 在 SQL 中代表“未知”,不是零、不是空字符串、也不是假,它无法参与任何确定性计算。
所以问题本质不是“为什么没返回 NULL”,而是“为什么 NULL 不被当作 0 处理”。
SUM() 遇到 NULL 时到底做了什么?
-
SUM()是聚合函数,设计目标是“对已知数值求和”; - 每一行的列值如果是
NULL,该行对应字段就不进入聚合输入集; - 这不是跳过再补 0,而是彻底排除——就像这行在这一列上“不存在”;
- 所以
SUM(amount)等价于 “把所有amount IS NOT NULL的行加起来”。
常见误解:
以为 SUM(NULL, 10, 20) = 30 是因为数据库“聪明地跳过了 NULL”;
实际是:SUM() 的输入集合只有 [10, 20],NULL 根本没传进去。
什么时候 SUM() 真的返回 NULL?
只有两种情况会返回 NULL:
- 查询结果集为空(比如
WHERE条件没匹配到任何行); - 所有参与聚合的值都是
NULL(即输入集合为空)。
例如:
SELECT SUM(amount) FROM orders WHERE status = 'cancelled' AND 1=0;→ 返回
NULL(空集)
SELECT SUM(amount) FROM orders WHERE id IN (999, 9999);→ 若这些
id 对应的 amount 全是 NULL,也返回 NULL这不是 bug,是 SQL 标准(ANSI SQL-92)强制要求的行为。
COALESCE(SUM(col), 0) 和 SUM(COALESCE(col, 0)) 差在哪?
关键区别在于 NULL 被处理的时机不同,语义完全不同:
-
COALESCE(SUM(col), 0):先聚合,再兜底。适用于“整组无数据时显示 0”,比如部门销售额为NULL表示“没查到记录”,你想填 0 展示; -
SUM(COALESCE(col, 0)):先逐行把NULL补成 0,再求和。适用于“每行都该计入,缺值即为 0”,比如库存表里某 SKU 的qty为NULL,业务定义就是“当前未录入,按 0 库存算”。
错误混用后果严重:
用 COALESCE(SUM(coupon_discount), 0) 处理 LEFT JOIN 后的优惠券字段,会把“没用券”的用户整行漏掉;正确做法是 SUM(COALESCE(coupon_discount, 0))。
运算表达式里 NULL 更危险:别怪 SUM(),怪减法
写 SUM(total - discount) 时,只要 total 或 discount 任一为 NULL,整条记录的差值就是 NULL → 被 SUM() 忽略。
这不是 SUM() 的问题,是 SQL 三值逻辑:100 - NULL → NULL,然后 SUM() 收到 NULL 就跳过。
必须拆开处理:
SUM(COALESCE(total, 0) - COALESCE(discount, 0))
但更要紧的是:先确认业务上 discount IS NULL 到底表示“没用券”还是“折扣金额未知”。后者补 0 可能歪曲事实。
真正麻烦的从来不是 NULL 本身,而是同一列中混着几种语义的 NULL —— 这时候一刀切 COALESCE(col, 0) 比不处理还危险。

















