AVG()对整数列求平均时在计算阶段即发生整数除法截断,导致小数丢失;修复需在除法前转为非整数类型,如AVG(CAST(col AS DECIMAL(10,2)))。

AVG() 对整数列求平均时,根本不是“显示丢失”,而是计算过程就截断了小数——连 ROUND() 都救不回来。
整数除法在 AVG 内部就发生了
AVG(x) 的数学定义是 SUM(x) / COUNT(x)。当 x 是 INT、TINYINT 等整数类型时,多数数据库(SQL Server、旧版 PostgreSQL、SQLite)会全程用整数算术执行这个除法:先算总和(仍是整数),再除以行数(整数除法),直接丢弃余数。比如 [80, 90, 99] 总和是 269,除以 3 得 89(不是 89.666…)。
这不是前端格式化问题,也不是结果类型没选对——值在聚合引擎内部就已经被截断了。
- MySQL 8.0 是个例外:它默认把整数列的
AVG()结果转成DECIMAL,但精度固定为 2 位小数(如3.00),仍可能不足 - PostgreSQL 15 返回
NUMERIC(1000,0),即整数结果,小数全无 - Oracle 和 SQL Server 行为类似,取决于列定义和版本
为什么 ROUND(AVG(col), 2) 没用
ROUND() 是对 AVG() 的输出做四舍五入,而如果 AVG() 已经返回了整数 89,那 ROUND(89, 2) 还是 89.00,无法还原被丢掉的 .666...。
真正要修复,必须在除法发生前,让至少一个操作数变成非整数类型:
-
AVG(CAST(col AS DECIMAL(10,2)))—— 显式、可控、跨库兼容,推荐用于报表 -
AVG(col * 1.0)—— 简洁,但 MySQL/PostgreSQL/SQL Server 对隐式转换后的小数位数处理不一致 -
AVG(CAST(col AS REAL))—— 适合大数据量,但要注意浮点误差(金额类慎用)
不同数据库对整数 AVG 的返回类型差异大
你写一条 SELECT AVG(id) FROM users;,在不同库里得到的结果类型可能完全不同:
- MySQL 8.0:返回
DECIMAL(p+2, 2),例如输入INT得DECIMAL(11,2) - PostgreSQL 15:返回
NUMERIC(1000,0),即无小数位的整数 - SQL Server:返回与输入列相同精度的
NUMERIC,但小数位常为 0 - SQLite:返回
REAL,但精度有限,且不支持CAST在聚合中提前干预
这意味着:同一句 SQL,在开发环境(PostgreSQL)跑出来带小数,在生产环境(SQL Server)却变成整数,问题直到上线才暴露。
最常被忽略的其实是数据清洗环节
AVG() 不会告诉你哪一行拖了后腿。它静默跳过 NULL,也静默把非法字符串(如 'N/A'、'-')转成 0 或报错,具体行为因库而异。
真要可信,得先确认:
-
COUNT(*)和COUNT(col)是否接近?差距大说明缺失严重 -
SELECT col FROM t WHERE col IS NOT NULL AND col !~ '^[0-9.+-eE]+$'(PostgreSQL)或等效正则,查混入的脏数据 - 是否所有非空值都该参与计算?比如
-1表示“超时”,业务上不该计入平均耗时
精度问题从来不是孤立的函数调用问题,而是数据类型、隐式转换、业务语义和数据库行为交织的结果——改一句 CAST 很快,但没看清这层,下次换库或加字段又踩坑。

















