round 默认采用银行家舍入法(四舍六入五成双),如 round(2.5)=2、round(3.5)=4;各库对负精度支持不一,postgresql/mysql支持d为负,sqlite不支持,sql server要求d为常量;ceil/floor仅接受数值,时间处理需先转时间戳再计算;负数取整方向易错,floor(-1.2)=-2;where中嵌套ceil/floor会导致索引失效,应改用范围查询或函数索引。

ROUND 为什么有时四舍五入结果和预期不一样?
因为 ROUND 在多数数据库里默认按「银行家舍入法」(四舍六入五成双)处理,不是数学意义上的四舍五入。比如 ROUND(2.5) 得 2,ROUND(3.5) 得 4——这在财务场景可能引发争议。
- PostgreSQL、SQL Server 默认用银行家舍入;MySQL 5.7+ 和 SQLite 的
ROUND(x)是传统四舍五入,但ROUND(x, d)(带精度)行为一致 - 想强制数学四舍五入:PostgreSQL 可用
ROUND(x::numeric + 0.5 - SIGN(x)*0.5),更稳妥是用CEIL(x - 0.5)(正数)或FLOOR(x + 0.5)(负数)组合 - 传入
NULL时,所有数据库都返回NULL,别忘了加COALESCE做兜底
CEIL 和 FLOOR 在时间计算中怎么避免边界错误?
直接对 timestamp 做 CEIL 或 FLOOR 会报错——它们只接受数值类型。常见误操作是把时间转成秒再算,但容易漏掉时区或精度丢失。
- 先用
EXTRACT(EPOCH FROM ts)(PostgreSQL)或UNIX_TIMESTAMP(ts)(MySQL)转为秒级数值,再套FLOOR(... / 3600) * 3600实现「向下取整到小时」 - MySQL 8.0+ 支持
DATE_SUB(ts, INTERVAL SECOND(ts) SECOND)更直观,但FLOOR配合时间戳转换更通用 - 注意:
FLOOR(-1.2)是-2,不是-1;负数取整方向容易反直觉,测试时务必覆盖负值场景
不同数据库对 ROUND 参数的支持差异
ROUND(x, d) 的第二参数 d 控制小数位数,但各库对 d 为负数的支持程度不一,这是线上出错高频点。
- PostgreSQL 和 MySQL 允许
d为负,如ROUND(1234.56, -2)→1200(向百位取整) - SQLite 不支持负的
d,会静默忽略或报错,得改用ROUND(x / 100.0) * 100模拟 - SQL Server 要求
d必须是常量表达式,不能是列名或子查询结果,否则报Incorrect syntax near ','
性能陷阱:嵌套 CEIL/FLOOR 导致索引失效
在 WHERE 条件里写 CEIL(price) = 10 或 FLOOR(created_at / 3600) = 172800,会让数据库无法使用 price 或 created_at 上的索引。
- 优先把函数移到等号右边:改成
price BETWEEN 9 AND 10(对应CEIL(price)=10),或用范围查询替代取整逻辑 - 如果必须用函数,考虑建函数索引(PostgreSQL/MySQL 8.0+):
CREATE INDEX idx_price_ceil ON t (CEIL(price)) - 注意:oracle 的
CEIL对number类型很高效,但对DECIMAL可能隐式转类型,查执行计划确认是否走索引
事情说清了就结束。数值函数看着简单,但跨库兼容性、负数行为、索引友好度这些点,上线前真得一条条对。