avg()忽略null值是ansi sql-92标准第10.9条强制规定,语义为剔除null后对非null值求和并除以count(col),全null时返回null;where过滤会破坏行上下文,而nullif仅转换值不影响其他聚合。

AVG() 忽略 NULL 值,不是数据库“偷偷优化”,而是 ANSI SQL-92 标准第 10.9 条明文规定的行为:NULL 表示未知,不能参与确定性算术运算。
你写 AVG(score),数据库不是“选择性跳过”,而是按标准语义执行:先剔除所有 score IS NULL 的行,再对剩余非 NULL 值求和并除以它们的个数(即 COUNT(score),不是 COUNT(*))。
这带来三个直接后果:
- 数据为
[10, NULL, 20, 0]时,结果是(10 + 20 + 0) / 3 = 10,0参与计算,NULL不参与 - 全列为
NULL时,AVG()返回NULL(不是0,也不是报错),因为分母COUNT(score)为 0,数学上无定义 -
WHERE score IS NOT NULL是冗余的——它不改变结果,但可能破坏同查询中其他聚合(如COUNT(*))或关联字段的上下文
AVG(NULLIF(col, 0)) 和 WHERE col != 0 的区别在哪
想排除 0(比如 API 耗时为 0 表示采集失败),别用 WHERE col != 0。
-
WHERE col != 0会整行过滤:该行从整个查询中消失,COUNT(*)、MAX(created_at)等都受影响 -
AVG(NULLIF(col, 0))只让0变成NULL,然后由AVG()自动跳过:行结构保留,其他聚合不受干扰
NULLIF(col, 0) 在 col = 0 时返回 NULL,否则返回原值。负值(如温度变化 -2.5)不会被误删,语义更安全。
COALESCE(AVG(col), 0) 和 AVG(COALESCE(col, 0)) 完全不是一回事
-
COALESCE(AVG(col), 0):只在整组全NULL(导致AVG()返回NULL)时兜底补0,不影响原始计算逻辑 -
AVG(COALESCE(col, 0)):先把每个NULL替成0,再算平均——分母变大,均值被拉低
典型误用:
- 订单表
amount为NULL表示“未下单”,却用AVG(COALESCE(amount, 0))→ 把“没下单”当成“下单金额为 0”,LTV 失真 - 传感器
temperature为NULL表示“设备离线”,填0后参与计算 → 引入虚假低温偏差
怎么确认 AVG() 的分母到底是多少
AVG() 不告诉你它除以几。同一列,COUNT(*)、COUNT(col)、COUNT(NULLIF(col, 0)) 可能完全不同:
-
COUNT(*):总行数 -
COUNT(col):非NULL行数(也就是AVG()的分母) -
COUNT(NULLIF(col, 0)):非NULL且非0的行数
必须显式查:SELECT AVG(score), COUNT(*), COUNT(score), COUNT(NULLIF(score, 0)) FROM students。光看平均值,永远不知道基数是否合理。
最常被忽略的,不是 NULL 本身,而是同一列里不同 NULL 的业务含义混在一起:有的是“未发生”,有的是“不适用”,有的是“录入失败”。统一用 COALESCE(col, 0) 反而掩盖了数据质量的真实问题。










