聚合函数默认跳过null,需据业务语义选择忽略、替换或告警;where中null比较需用is null或coalesce;count(*)与count(col)行为不同;补全维度需left join,而非仅coalesce。

聚合函数默认跳过 NULL,但不显式处理会导致统计口径错位——比如 SUM 看似“安全”,实则漏掉本应计为 0 的业务逻辑场景。
WHERE 条件里写 col != 'done' 却查不到 NULL 行?
这是最常踩的坑:MySQL 中 NULL != 'done' 返回 NULL(即逻辑上“未知”),不满足 WHERE 的 TRUE 条件,整行被过滤掉。
- 错误写法:
WHERE status != 'done'→ 漏掉所有status IS NULL的记录 - 正确写法:
WHERE status != 'done' OR status IS NULL,或更清晰地用WHERE COALESCE(status, 'unknown') != 'done' - 如果业务上
NULL就代表“未开始”,那它和'pending'是等价状态,必须统一映射后再比较
SUM、AVG 忽略 NULL,但有时你想要“算作 0”
比如统计每日销售额:sales_amount 为 NULL 可能表示当天没开单,但业务要求“没开单 = 0 元”,直接 SUM(sales_amount) 会少算天数。
- 用
COALESCE(sales_amount, 0)包一层再聚合:SUM(COALESCE(sales_amount, 0)) -
IFNULL更短,但只支持两个参数,适合简单 fallback:SUM(IFNULL(sales_amount, 0)) - 注意类型兼容:若
sales_amount是DECIMAL,别用IFNULL(sales_amount, ''),会触发隐式转换报错
COUNT 的两种写法结果完全不同
COUNT(*) 和 COUNT(col) 行为差异极大,混用极易导致分母错误。
-
COUNT(*)统计所有行,含NULL值所在行 -
COUNT(sales_amount)只统计sales_amount IS NOT NULL的行数 - 算平均单价时,如果写成
SUM(price) / COUNT(*),而部分price是NULL,分母就比实际参与计算的分子行数多 → 结果偏小 - 稳妥做法:
SUM(price) / NULLIF(COUNT(price), 0),避免除零;或直接用AVG(price)(它内部已忽略 NULL)
聚合后想补全 NULL 值的统计维度
比如按部门查平均薪资,但某些部门没人(整组数据为空),GROUP BY dept_id 不会返回该部门一行,更不会返回 AVG = NULL —— 它直接消失。
- 这不是 NULL 处理问题,而是数据存在性问题;需用
LEFT JOIN补全维度表 - 若只是想把聚合结果里的
NULL显式转成 0,仍用COALESCE(AVG(salary), 0) - 特别注意:
COALESCE作用于聚合后结果,不是聚合前;顺序不能颠倒
真正容易被忽略的是:NULL 在聚合中不是“被处理掉了”,而是“被逻辑排除了”。你得先确认业务语义——这个 NULL 是该忽略、该替换、还是该报警。否则加再多 COALESCE 也救不回错位的统计口径。











