round有时四舍五入结果不对,因其默认采用银行家舍入法(四舍六入五成双),如round(2.5)得2、round(3.5)得4;mysql 8.0+支持round(x,d,1)实现传统四舍五入,postgresql需trunc(x+sign(x)*0.5)绕行,sql server默认传统但需防浮点误差。

ROUND 为什么有时四舍五入结果不对?
因为 ROUND 在多数数据库(如 MySQL、PostgreSQL)中默认使用「银行家舍入法」(四舍六入五成双),不是数学意义上的四舍五入。比如 ROUND(2.5) 得 2,ROUND(3.5) 得 4——这和直觉冲突,但符合 IEEE 754 标准。
- MySQL 8.0+ 支持显式指定模式:
ROUND(x, d, 0)是银行家舍入,ROUND(x, d, 1)才是传统四舍五入(但仅限 MySQL) - PostgreSQL 不支持第三参数,想强制四舍五入得绕道:
TRUNC(x + SIGN(x) * 0.5)(注意处理负数) - SQL Server 的
ROUND默认就是传统四舍五入,但要注意:它对DECIMAL类型的精度截断发生在内部计算阶段,可能引发隐式转换误差 - 如果字段是
FLOAT或REAL,先转DECIMAL再ROUND,否则浮点误差会放大舍入偏差,例如ROUND(1.235, 2)可能返回1.23而非1.24
CEIL 和 FLOOR 在负数场景下容易搞反
CEIL 向正无穷取整,FLOOR 向负无穷取整——不是“向上/向下”这种方向感描述能准确覆盖的。比如 CEIL(-1.2) 是 -1(比 -1.2 大的最小整数),FLOOR(-1.2) 是 -2(比 -1.2 小的最大整数)。
- 别用“向上取整”“向下取整”去记,直接代入负数测试一次:
SELECT CEIL(-0.1), FLOOR(-0.1)→ 结果分别是 0 和 -1 - 分页计算常用
CEIL(total / page_size),但如果total是负数(逻辑错误导致),结果会完全失真,建议加CASE WHEN total > 0 THEN CEIL(...)防御 - 某些旧版 SQLite 没有
CEIL函数,得用-FLOOR(-x)模拟,但要注意 NULL 输入时两者行为一致,而模拟写法在 x 为整数时多一次取负运算
不同数据库对小数位数参数的处理差异很大
ROUND(x, d) 的第二个参数 d 表示保留小数位数,但各数据库对 d 为负数、NULL 或超出精度范围的反应完全不同。
- MySQL 允许
d为负数:ROUND(1234.56, -2)→ 1200;PostgreSQL 同样支持;但 SQL Server 报错,必须用ROUND(x, d, 1)配合CAST绕过 - 如果
d是NULL,MySQL 返回NULL;PostgreSQL 报错;SQL Server 返回原值——跨库迁移时这里极易漏测 - 当
x是DECIMAL(10,2),而你写ROUND(x, 4),PostgreSQL 会自动升为DECIMAL(10,4),但 MySQL 可能截断为DECIMAL(10,2)并丢掉精度,需显式CAST(ROUND(x, 4) AS DECIMAL(10,4))
性能陷阱:在 WHERE 条件里用 ROUND 等函数会失效索引
写 WHERE ROUND(price, 0) = 100,哪怕 price 字段有索引,绝大多数数据库也无法走索引扫描,因为函数改变了原始值分布。
- 替代方案是改写为范围查询:
WHERE price >= 99.5 AND price (等价于 <code>ROUND(price, 0) = 100) - 如果必须用函数,考虑生成列(MySQL 5.7+/PostgreSQL 12+):
ALTER TABLE t ADD COLUMN price_rounded INT AS (ROUND(price, 0)) STORED,再给该列建索引 -
CEIL和FLOOR同样适用此规则,例如FLOOR(created_at)无法利用created_at的时间索引,应改用created_at >= '2024-01-01' AND created_at
实际用的时候,最常出问题的不是函数本身,而是没意识到「同名函数在不同数据库里行为不一致」,尤其当项目要兼容 MySQL 和 PostgreSQL 时,ROUND 的第三参数、CEIL 对负零的处理、甚至 FLOOR(-0.0) 返回 -0 还是 0 都可能不同。别依赖文档里的“标准描述”,上线前一定在目标环境实测边界值。










