ROUND默认采用银行家舍入(四舍六入五成双),如ROUND(2.5,0)得2;TRUNCATE是直接截断不进位,如TRUNCATE(3.999,2)得3.99;各数据库对ROUND和TRUNCATE函数支持及行为存在差异。

ROUND 函数怎么保留小数位,为什么有时结果不是预期的四舍五入?
SQL 的 ROUND 默认是「四舍六入五成双」(银行家舍入),不是数学上简单的四舍五入。尤其在金融或报表场景下,容易误判结果。
-
ROUND(2.5, 0)→2(不是3),因为 2 是偶数;ROUND(3.5, 0)→4 - 只在 PostgreSQL、SQL Server、Oracle 等部分数据库中默认启用银行家舍入;MySQL 5.7+ 和 SQLite 是传统四舍五入,但 MySQL 8.0+ 在某些精度下也会受浮点表示影响
- 传入的数值如果是浮点类型(如
DOUBLE),先有精度损失,再舍入——比如ROUND(1.235, 2)可能变成1.23而非1.24,本质是1.235在二进制中无法精确表示 - 安全做法:确保输入是精确数值类型,如
DECIMAL(10,3),再用ROUND(col, 2)
TRUNCATE 和 ROUND 的核心行为差异在哪?
TRUNCATE 不是舍入,是截断——它直接砍掉多余小数位,不看下一位数字,也不加任何进位逻辑。
-
TRUNCATE(3.999, 2)→3.99;ROUND(3.999, 2)→4.00 -
TRUNCATE(-1.999, 1)→-1.9;ROUND(-1.999, 1)→-2.0(注意负数方向) - MySQL 支持
TRUNCATE()函数;PostgreSQL 没有原生TRUNCATE数值函数,得用TRUNC()(注意函数名不同);SQL Server 用ROUND(x, d, 1)第三个参数为1表示截断模式 - 别把
TRUNCATE TABLE和这个搞混——那是 DDL 命令,和数值截断完全无关
不同数据库里 ROUND 的参数顺序和兼容性坑
绝大多数 SQL 方言中 ROUND 是 ROUND(numeric_expression, length),但 length 的含义和符号意义容易出错。
- length > 0:保留小数点后多少位,如
ROUND(123.456, 2)→123.46 - length = 0:四舍五入到整数位,如
ROUND(123.456, 0)→123 - length ROUND(123.456, -1) →
120;ROUND(123.456, -2)→100 - SQLite 的
ROUND不支持负数 length;PostgreSQL 对负数 length 支持良好;MySQL 全支持,但要注意版本 —— 5.6 之前对负数 length 的处理略有偏差 - 如果字段是字符串类型(如
VARCHAR存数字),先显式转成数值:ROUND(CAST(price_str AS DECIMAL(10,2)), 2),否则可能报错或静默失败
什么时候该用 ROUND,什么时候必须换方案?
当业务明确要求「无条件向上/向下取整」「固定截断」「或需要控制舍入方向」时,ROUND 就不够用了。
- 要「始终向上进一」:用
CEIL()或CEILING()(各库命名略有不同) - 要「始终向下舍去」:用
FLOOR() - 要「正数向下、负数也向下(即向零截断)」:MySQL 用
TRUNCATE(x, d);PostgreSQL 用TRUNC(x, d);SQL Server 用ROUND(x, d, 1) - 涉及金额运算,建议全程使用
DECIMAL类型存储,避免浮点列参与ROUND——哪怕写ROUND(CAST(col AS DECIMAL(15,4)), 2)也比直接ROUND(col, 2)可靠
最常被忽略的一点:ROUND 的行为依赖底层数值类型的精度表达能力,而不是你写的字面量看起来多精确。










