ROUND(SUM(x), n)先求和再四舍五入,符合会计准则;SUM(ROUND(x, n))逐行舍入再求和,导致误差累积;且ROUND结果保留源类型精度,需CAST显式截断尾零。

ROUND(SUM(x), n) 和 SUM(ROUND(x, n)) 有本质区别
前者先求和再四舍五入,是财务报表中推荐的安全做法;后者对每行单独 ROUND 再求和,会导致误差累积。比如原始值 [1.355, 1.355],若用 SUM(ROUND(x, 2)) 得到 1.36 + 1.36 = 2.72,而真实和 2.71 经 ROUND(2.71, 2) 应为 2.71——差了 0.01。
关键点在于:聚合精度控制必须在聚合函数外层做,不能塞进参数里干扰计算逻辑。
为什么 ROUND(sum_col, 2) 返回 12.350 而不是 12.35
这不是 bug,是类型继承行为:如果 sum_col 是 DECIMAL(10,3) 或浮点类型,ROUND 结果仍保留源类型的小数位宽。数据库不会自动“格式化”输出。
- 想彻底截断尾随零?必须显式
CAST(ROUND(sum_col, 2) AS DECIMAL(10,2)) - MySQL 用户要特别注意:某些版本对
ROUND(2.15, 1)可能返回2.1(二进制浮点误差),建议原始数据就用DECIMAL存储 - Oracle/PostgreSQL 行为更稳定,但同样不解决显示问题——前端要 “12.35” 字符串,还得靠
TO_CHAR或应用层处理
GROUP BY 时用 ROUND 做分组键的风险
浮点误差会让本该相等的值在 ROUND 后分裂成不同组。例如 1.005 和 1.0049999999999999 在 IEEE 754 下存储不同,ROUND(x, 2) 可能分别得 1.01 和 1.00。
更可靠的做法:
- 分组前先把数值转成整数单位(如金额存“分”,再
GROUP BY amount_cents / 100) - 或用
CAST(x AS DECIMAL(10,2))强制精度对齐后再分组 - 避免用
ROUND(price, 2)直接当分组字段,尤其当原始列是FLOAT或DOUBLE
除法运算中小数位丢失的根本原因
SQL Server 和 MySQL 默认整数除法会截断小数(如 10/3 → 3),这不是 ROUND 能补救的——ROUND 接收的是已经截断的结果。
正确姿势:
- 强制至少一个操作数为高精度小数:
CAST(a AS DECIMAL(10,4)) / b - 别依赖
ROUND(a/b, 2)来“修复”精度,它只是对错误结果再四舍五入 - 建表时就该用
DECIMAL(p,s)存原始值,而不是靠查询时 ROUND 挽救
真正难的从来不是怎么写 ROUND,而是意识到它只管“算出来怎么显示”,不管“算的过程准不准”。精度问题必须从数据类型、存储方式、运算顺序三层一起控。

















