avg()对整数列求平均时因整数除法截断小数导致结果失真,如80、90、99平均本应为89.666…却得89;必须用cast(列 as decimal)或列*1.0提前提升精度,而非依赖round事后修正。

AVG() 在整型列上直接使用,结果会静默截断小数——不是显示问题,是计算过程就丢了精度。
AVG() 整数除法为什么返回错误数值?
因为 AVG() 内部先 SUM() 再 COUNT(),而整数列的 SUM() 和 COUNT() 都是整数,整数除整数在多数数据库(SQL Server、PostgreSQL、旧版 MySQL)中默认执行整数除法,直接丢弃小数部分。例如 [80, 90, 99] 的平均本应是 89.666...,但结果可能是 89。
这不是四舍五入,也不是显示格式问题;ROUND(AVG(col), 2) 完全无效,因为输入已经是被截断后的整数。
- MySQL:对整数列返回
DECIMAL,但默认精度常不足(如只保留 1 位小数) - SQL Server / PostgreSQL:直接返回整数类型,小数全丢
- Oracle:行为依赖列定义和版本,不可靠
怎么让 AVG() 返回带小数的正确结果?
核心是干预除法发生前的数据类型——必须让分子(SUM)或分母(COUNT)至少一方是浮点/高精度类型。
-
AVG(CAST(amount AS DECIMAL(10,2))):显式控制精度,报表类场景首选 -
AVG(amount * 1.0):简洁,兼容性好,但小数位数由数据库隐式决定(MySQL 可能给 1 位,SQL Server 给 6 位) -
AVG(CAST(amount AS FLOAT)):适合科学计算,但金额类字段慎用(浮点误差风险) - 窗口函数同理:
AVG(CAST(salary AS DECIMAL(10,2))) OVER (PARTITION BY dept)
为什么除法表达式也得改,比如 success_count / total_count?
所有显式除法都受同一规则约束:两个整数相除,结果必为整数。例如 5 / 10 得 0,不是 0.5。
- 错:
success_count / total_count * 100→ 结果恒为 0(当 success_count - 对:
success_count * 1.0 / NULLIF(total_count, 0) * 100→* 1.0提升分子精度,NULLIF防除零 - 更严谨:
CAST(success_count AS DECIMAL(10,2)) / NULLIF(total_count, 0) * 100
最容易被忽略的细节
这个陷阱不报错、不告警,只静默返回错误数值。上线后才发现“平均单价”系统性偏低、“完成率”批量为 0%,排查成本远高于写 SQL 时多敲几个字符。真正关键的不是记住函数怎么写,而是每次遇到数值计算,下意识问一句:“参与运算的字段是什么类型?”










