AVG()计算结果偏差主因是NULL值被自动忽略、整数除法截断小数、非数值列未转换及业务语义误判;正确做法是结合COUNT验证分母、用CAST或乘1.0提升精度、对字符串先NULLIF再CAST,并明确NULL与0的业务含义。

AVG() 计算结果不对,几乎从来不是函数本身出错,而是你没意识到它在 quietly 跳过 NULL、静默截断小数、或把字符串当数字用。
AVG() 自动跳过 NULL,但业务上可能需要“按总数算”
比如 5 条记录里有 2 条 score 是 NULL,AVG(score) 实际只算剩下 3 条——分母是 3,不是 5。这和“所有用户平均分”的业务口径常不一致。
- 现象:统计订单平均金额时,大量未支付订单存为
NULL,导致均值虚高 - 误用:
AVG(COALESCE(score, 0))把 NULL 当 0 算,人为拉低均值(除非业务明确要求“未评分=0分”) - 更稳妥做法:
AVG(score)+ 单独查COUNT(*)和COUNT(score)对比,确认缺失比例是否合理 - 真要补零且语义成立,用
WHERE score IS NOT NULL显式过滤,比 COALESCE 更透明
INT 列的 AVG 直接整数除法,小数位根本没参与计算
字段类型是 TINYINT、SMALLINT 或 INT 时,多数数据库(SQL Server、旧版 PostgreSQL)先算总和(仍是整数),再整除行数——80+90+99 = 269,269 / 3 = 89,不是 89.666…
- MySQL 默认返回
DECIMAL,但精度可能只有 1 位(显示为89.7),其实是底层已截断 - 别写
ROUND(AVG(score), 2)——输入已经是整数,ROUND 没东西可四舍五入 - 必须在除法前干预:用
AVG(CAST(score AS DECIMAL(10,2)))或AVG(score * 1.0) -
score * 1.0在 MySQL/PostgreSQL 可用,但浮点误差风险存在(如0.1 + 0.2 ≠ 0.3)
对非数值列直接调用 AVG,数据库不会自动转换
AVG(price) 没问题,但 AVG(price_str)(类型是 VARCHAR)会直接报错,例如 PostgreSQL 提示 function avg(text) does not exist。
- 常见场景:导出数据时把数字存成字符串,或字段含单位(如
"120kg") - 安全转换:
AVG(CAST(NULLIF(price_str, '') AS NUMERIC)),先剔空字符串再转数值 -
NULLIF(price_str, '')把空字符串转成NULL,让AVG()自动忽略;COALESCE(price_str, '0')是反模式——把空串当 0 算,扭曲结果 - 日期列不能直接
AVG(created_at),得先转时间戳:AVG(EXTRACT(EPOCH FROM created_at))
GROUP BY 或窗口函数写错,AVG 算的压根不是你想的那个“平均”
SELECT store_id, AVG(order_amount) FROM orders 不加 GROUP BY store_id,结果只有一行,store_id 值随机(MySQL 8.0 严格模式直接报错)。
- 窗口函数漏括号:
AVG(sales) OVER语法错误;必须写AVG(sales) OVER() - 加了
ORDER BY:AVG(sales) OVER(ORDER BY id)变成累积平均,不是全局均值 - WHERE 在窗口函数之前执行——
AVG() OVER()算的是 WHERE 过滤后的数据集均值,不是原始全表 - 分组字段与 SELECT 不对齐:写
SELECT store_name, AVG(amount) FROM orders GROUP BY store_id,MySQL 可能返回错乱store_name,其他数据库直接拒绝
最麻烦的从来不是怎么写 AVG,而是你是否清楚这一列里混着 NULL、0、空字符串、“N/A”、负数,以及业务规则到底要求“排除缺失值”,还是“缺失即 0”,或是“该字段本不该有缺失”。函数永远诚实,数据语义才容易骗人。

















