avg()默认跳过null值,无需where过滤;其分母为count(col)而非count(*),0参与计算而null不参与;coalesce(avg(col),0)仅结果兜底,avg(coalesce(col,0))将null转0拉低均值;排除0值应使用nullif(col,0)而非where。

AVG() 默认就跳过 NULL,不需要额外处理
是的,AVG() 会自动忽略 NULL 值——这不是某个数据库的“特性”,而是 ANSI SQL-92 标准第 10.9 条明确定义的行为。它先过滤掉该列值为 NULL 的行,再对剩余非 NULL 值求和并除以它们的个数。
常见错误现象:
- 写
SELECT AVG(col) FROM t WHERE col IS NOT NULL—— 多余,结果和不加WHERE完全一样 - 以为
AVG()返回NULL是“出错了”——其实是正常语义:全NULL组无有效值,数学上无定义
关键点:
-
AVG(col)等价于SUM(col) / COUNT(col),分母是COUNT(col)(非NULL行数),不是COUNT(*) - 若数据为
[10, NULL, 20, 0],结果是(10 + 20 + 0) / 3 = 10,0参与计算,NULL不参与 - 全
NULL时返回NULL,不是0,也不是报错
COALESCE(AVG(col), 0) 和 AVG(COALESCE(col, 0)) 区别在哪
这两个写法语义完全不同,选错会导致业务逻辑错位。
COALESCE(AVG(col), 0) 是结果兜底:只在整组无有效值(即 AVG() 返回 NULL)时补 0,不改变原始聚合逻辑;
AVG(COALESCE(col, 0)) 是数据预处理:先把所有 NULL 替换成 0,再算平均——这会让分母变大、均值被拉低,仅适用于“未发生=0”且经业务确认的场景。
典型误用:
- 销售表中
amount为NULL表示“订单未创建”,却用AVG(COALESCE(amount, 0))→ 把“没下单”当成“下了单但金额为 0”,LTV 模型失真 - 传感器温度字段
temperature为NULL表示“设备离线”,填0后参与计算 → 引入虚假低温偏差
更安全的做法是显式分离统计:COUNT(*) AS total, COUNT(amount) AS valid_count, AVG(amount) AS avg_valid。
想排除 0 值而不是 NULL?用 NULLIF(),别用 WHERE
当 0 表示无效采集(如 API 耗时为 0、测试订单金额为 0),而字段本身又允许 NULL,直接 WHERE col != 0 会整行过滤,破坏分组上下文。
正确做法是让 0 “变成 NULL”,再由 AVG() 自动跳过:
-
AVG(NULLIF(col, 0)):当col = 0时返回NULL,否则返回原值 - 对比
WHERE col != 0:前者保留行结构,不影响同查询中其他聚合(如COUNT(*)或关联字段);后者丢弃整行 - 负值必须保留时(如温度变化),不能用
!= 0过滤,否则会误删负数
怎么知道 AVG() 的分母到底是多少
光看 AVG() 结果看不出它基于几条记录算的——这是平均值失真的最隐蔽原因。
必须同步查 COUNT() 才能验证基数:
-
COUNT(col):实际参与AVG()计算的非NULL行数(即分母) -
COUNT(*):总行数(含全NULL行) -
COUNT(NULLIF(col, 0)):排除0后的非NULL行数(用于验证NULLIF()效果)
推荐一次性查清:SELECT AVG(x), COUNT(x), COUNT(*), COUNT(NULLIF(x, 0)) FROM t;嵌套子查询或视图里用 AVG() 时,更要在外层校验这些计数。
最容易被忽略的是:NULL 不是空字符串、不是 0、不是缺失默认值,它是“未知”。强行用 COALESCE 填值前,得先确认这个“未知”在业务里到底代表什么。











