ROUND()必须包裹在SUM()外层,即ROUND(SUM(amount), 2),而非SUM(ROUND(amount, 2));后者导致每行先四舍五入再累加,违背“先求和、再四舍五入”的财务准则,引发累计误差。

ROUND() 必须在 SUM() 外层包裹,不能放里面
直接写 SUM(ROUND(amount, 2)) 看似合理,但会导致每行先四舍五入再累加,和“先求和、再四舍五入”的业务要求不符。比如三笔金额 1.445、1.445、1.445,真实总和是 4.335,应四舍五入为 4.34;而先 ROUND 再 SUM 会变成 1.44 + 1.44 + 1.44 = 4.32,差了 0.02。
正确做法是把 ROUND 放在 SUM 外面:
SELECT ROUND(SUM(amount), 2) AS total_amount FROM orders;
不同数据库对 ROUND() 的小数位处理略有差异
MySQL 和 PostgreSQL 默认使用“四舍六入五成双”(银行家舍入),但多数业务场景需要传统四舍五入。PostgreSQL 中需显式指定 ROUND(amount, 2) 即可;MySQL 5.7+ 同样支持,但旧版本或严格模式下可能截断而非舍入——务必确认 sql_mode 不含 STRICT_TRANS_TABLES 或 ERROR_FOR_DIVISION_BY_ZERO 等干扰项。
- SQL Server 没有原生
ROUND对 DECIMAL 的安全重载,建议用CONVERT(DECIMAL(18,2), SUM(amount))替代,避免浮点误差 - Oracle 中
ROUND行为一致,但要注意字段是否为NUMBER类型,若为BINARY_FLOAT可能引入精度丢失
聚合前就存在浮点数,ROUND 也无法挽救精度问题
如果原始 amount 是 FLOAT 或 REAL 类型(比如从应用层传入未格式化的 JS 数字),SUM 结果本身已失真。例如 0.1 + 0.2 在浮点下不等于 0.3,此时 ROUND(SUM(amount), 2) 可能返回 0.30 或 0.31,取决于底层二进制表示。
解决路径只有两个:
- 建表时把金额字段定义为
DECIMAL(18,2)或等价类型(如 PostgreSQL 的NUMERIC) - 插入前由应用层转成字符串再入库,或用
CAST('123.45' AS DECIMAL)强制转换
GROUP BY 场景下别漏掉 ROUND 的位置
当需要按用户、日期分组汇总时,容易误写成 SUM(ROUND(amount, 2))。同样,这仍是逐行舍入。正确结构是:
SELECT user_id, ROUND(SUM(amount), 2) AS total_spent FROM orders GROUP BY user_id;
注意:若后续还要做二次计算(比如算占比),建议保留更高精度的中间值,仅展示层用 ROUND ——否则累计误差会放大。
最常被忽略的是数据类型隐式转换:哪怕你写了 ROUND(SUM(amount), 2),如果 amount 是 INT,SUM 结果仍是整数,ROUND 不起作用;必须确保原始字段或 SUM 结果至少是 DECIMAL 或带小数位的数值类型。

















