最稳妥方式是用COALESCE(SUM(col), 0)在聚合外层兜底;因SQL标准规定SUM/COUNT/AVG等天然忽略NULL,整组无数据或全NULL时返回NULL,而非0,故必须显式处理空结果集。

聚合统计中遇到 NULL,直接加默认值最稳妥的方式是用 IFNULL(MySQL)或 ISNULL(SQL Server),但二者行为不完全等价,不能无脑替换。
为什么 COUNT/SUM/AVG 本身不自动处理 NULL?
SQL 标准规定:聚合函数天然忽略 NULL 值——COUNT(col) 只计非空行,SUM(col) 跳过 NULL 项,AVG 同理。这导致两类典型问题:
- 分组后某组全为
NULL,SUM()返回NULL,而非 0 - LEFT JOIN 后右表字段为空,聚合时带入
NULL,结果列变成NULL
此时需要显式兜底,而不是依赖聚合函数“自动补 0”。
MySQL 用 IFNULL 最直接
IFNULL(expr1, expr2) 是 MySQL 专属,语义清晰:当 expr1 为 NULL 时返回 expr2,否则返回 expr1。常套在聚合外层:
SELECT dept, IFNULL(SUM(salary), 0) AS total_salary, IFNULL(AVG(age), 0) AS avg_age FROM employees GROUP BY dept;
注意点:
-
IFNULL只接受两个参数,不能链式嵌套(想多层判断得用CASE WHEN) - 第二个参数类型需与第一个兼容,比如
IFNULL(SUM(salary), 'N/A')会触发隐式转换,可能报错或失真 - 对空表或全
NULL分组,SUM()返回NULL,IFNULL才真正生效
SQL Server 用 ISNULL 要小心类型截断
ISNULL(check_expression, replacement_value) 是 SQL Server 函数,但和 IFNULL 关键区别在于:返回值类型**严格取自第一个参数的类型**。例如:
SELECT ISNULL(SUM(credit), 0) FROM accounts;
如果 credit 是 DECIMAL(10,2),那么 ISNULL 返回的仍是 DECIMAL(10,2);但如果写成:
SELECT ISNULL(SUM(credit), 0.0)
就可能因字面量精度不同引发意外截断。更安全的做法是显式 cast 或统一用 COALESCE(标准 SQL,三端兼容):
-
COALESCE(SUM(credit), 0)类型推导更合理,且支持多参数 -
ISNULL性能略优于COALESCE(单次求值),但差异微乎其微,可忽略 - PostgreSQL 没有
IFNULL或ISNULL,必须用COALESCE或COALESCE(SUM(x), 0)
聚合前还是聚合后加默认值?
绝大多数场景应加在**聚合函数外层**,即 IFNULL(SUM(x), 0),而非 SUM(IFNULL(x, 0))。区别在于语义:
-
IFNULL(SUM(x), 0):整组求和结果为NULL(比如该组无记录)时补 0 -
SUM(IFNULL(x, 0)):先把每行x的NULL替换为 0,再求和(适合“缺值视为 0”的业务逻辑)
两者结果可能一致,但意图完全不同。比如统计每日订单金额,若某天没订单:
- 用
IFNULL(SUM(amount), 0)→ 正确返回 0 - 用
SUM(IFNULL(amount, 0))→ 因无行可 sum,仍得NULL,没解决问题
真正容易被忽略的是:空分组(如 GROUP BY 字段全为 NULL 或无匹配行)下,SUM 一定返回 NULL,这时外层 IFNULL 才是最后一道防线。

















