SQL 数值函数 ROUND、CEIL、FLOOR 使用技巧

1次阅读

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会导致索引失效,应改用范围查询或函数索引。

SQL 数值函数 ROUND、CEIL、FLOOR 使用技巧

ROUND 为什么有时四舍五入结果和预期不一样?

因为 ROUND 在多数数据库里默认按「银行家舍入法」(四舍六入五成双)处理,不是数学意义上的四舍五入。比如 ROUND(2.5)2ROUND(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 在时间计算中怎么避免边界错误?

直接对 timestampCEILFLOOR 会报错——它们只接受数值类型。常见误操作是把时间转成秒再算,但容易漏掉时区或精度丢失。

  • 先用 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) = 10FLOOR(created_at / 3600) = 172800,会让数据库无法使用 pricecreated_at 上的索引。

  • 优先把函数移到等号右边:改成 price BETWEEN 9 AND 10(对应 CEIL(price)=10),或用范围查询替代取整逻辑
  • 如果必须用函数,考虑建函数索引(PostgreSQL/MySQL 8.0+):CREATE INDEX idx_price_ceil ON t (CEIL(price))
  • 注意:oracleCEILnumber 类型很高效,但对 DECIMAL 可能隐式转类型,查执行计划确认是否走索引

事情说清了就结束。数值函数看着简单,但跨库兼容性、负数行为、索引友好度这些点,上线前真得一条条对。

text=ZqhQzanResources