AVG(score)的分母是COUNT(score),不是COUNT(*);它等价于SUM(score)/COUNT(score),仅统计非NULL行数,静默跳过NULL导致基数塌陷,易偏离业务口径。

AVG(score) 的分母到底是 COUNT(score) 还是 COUNT(*)?
AVG() 从不除以总行数。它的实际计算逻辑是 SUM(score) / COUNT(score),而 COUNT(score) 只统计非 NULL 值的行数。这意味着只要表里有 score IS NULL 的记录,分母就自动缩水——比如 10 行数据含 3 个 NULL,COUNT(score) 是 7,COUNT(*) 是 10,结果偏差近 30%。
这个过程完全静默:没警告、不报错、也不提示你“我跳过了 3 行”。你看到的只是一个看似合理的数字,背后基数已塌陷。
- 别依赖直觉判断“这组该有多少人”,必须显式查出
COUNT(score)和COUNT(*)并列对比 - 在
GROUP BY场景下,分母边界更易混淆;嵌套子查询后,外部 WHERE 过滤会进一步改变分母范围 - 如果业务口径是“全体用户平均分”,那真正该用的是
SUM(score) / COUNT(*),而不是AVG(score)
COALESCE(score, 0) 真的是补全 NULL 吗?
不是补全,是语义篡改。COALESCE(score, 0) 把 “未知” 强行变成 “已知为零”,但业务上这两者天差地别:
- 传感器表中
temperature IS NULL表示设备离线,填 0 会系统性拉低均值 - 员工表中
salary IS NULL可能是“未定薪”或“合同工无底薪”,填 0 就等于认定他们零收入 - 更隐蔽的问题:
AVG(COALESCE(score, 0))的分母仍是COUNT(score),不是COUNT(*),所以它 ≠SUM(COALESCE(score, 0)) / COUNT(*)
除非业务明确定义“NULL = 0”,否则别用 COALESCE 掩盖问题。
空结果集或全 NULL 时,AVG() 返回 NULL 怎么办?
SELECT AVG(score) FROM ratings WHERE user_id = 999 这种查询,若无匹配数据,结果就是 NULL。这在应用层极易引发异常:
- Java 中
ResultSet.getDouble()直接抛SQLException - Python 中
row[0]是None,后续参与算术运算触发TypeError - 前端渲染显示为
null字符串或空白,而非“暂无数据”
必须兜底:IFNULL(AVG(score), 0)(MySQL)或 COALESCE(AVG(score), 0)(通用),但要注意:这只是防崩,不解决语义问题。
想排除 0 值(而非 NULL)时,WHERE 和 CASE 哪个更可靠?
0 值和 NULL 是两回事。AVG() 自动忽略 NULL,但默认把 0 当有效值参与计算。若 0 表示“测试单”“埋点失败”或“非法评分”,就得主动剔除:
- 最简单可靠的是
WHERE score != 0,前提是字段类型允许负值;若只关心正数,可用WHERE score > 0 - 若字段可能为 NULL,且你要排除 0 和 NULL,写成
WHERE score IS NOT NULL AND score != 0 - 慎用
AVG(CASE WHEN score != 0 THEN score END):虽然效果等价,但可读性差;一旦误写成ELSE 0,就又把 0 拉回计算了
关键不在语法怎么写,而在确认“这个 0 到底是不是真实业务值”——翻文档、问产品、查埋点规范,比调 SQL 更重要。










