floor和ceiling仅作数值截断,无法表达复杂阶梯逻辑;适合等宽分段或向上计费场景,但不规则档位必须用case或查找表实现。

为什么直接用 FLOOR 或 CEILING 算阶梯价会出错
因为 FLOOR 和 CEILING 本身只做数值截断,不带业务逻辑。比如想实现“每满100减5”,你不能写 FLOOR(price / 100) * 5 就完事——它在 price = 99 时得 0,price = 100 时突变成 5,看似对,但一旦阶梯规则变成“100–199档减5,200–299档减12”,纯 FLOOR 就无法表达区间映射。
真正要的是“把价格映射到预设档位”,而 FLOOR 和 CEILING 只是辅助工具,不是解决方案本身。
用 FLOOR 实现等宽价格分段(如每50元一档)
适合规则简单、档距固定、优惠值仅依赖档位编号的场景,例如:每满50元打95折,即 price × 0.95^(FLOOR(price/50))。
-
FLOOR(price / 50)把 0–49→0,50–99→1,100–149→2……转换成档位索引 - 注意除数必须为正,且
price为DECIMAL或NUMERIC类型,避免整数除法截断(如 PostgreSQL 中99/50得 1,但 SQL Server 默认是整数除,需先转CAST(price AS DECIMAL)) - MySQL 8.0+、PostgreSQL、SQL Server 2012+ 都支持,但 SQLite 的
FLOOR是浮点函数,输入整数会隐式转REAL,精度无问题但需确认字段类型是否含小数
示例(PostgreSQL):
SELECT name, price, FLOOR(price / 50.0) AS tier, price * POWER(0.95, FLOOR(price / 50.0)) AS discounted_price FROM products;
用 CEILING 处理“向上取整计费”类阶梯(如运费、最小起订量)
典型场景是“不足100g按100g计”,对应 CEILING(weight / 100.0) * 100;或“订单金额不满300不包邮”,可配合 CASE 判断:CEILING(total / 300.0) = 1 AND total 。
-
CEILING对负数行为各库不一致:SQL Server 返回 -0(实际为 0),PostgreSQL 返回 -0.0,MySQL 返回 -0 —— 但价格不会是负数,所以只要确保price字段CHECK (price >= 0)即可规避 - 别写
CEILING(price / 100)(缺小数点),在整数上下文中可能被当作整数除,结果恒为 1(如 price=50 → 50/100=0 → CEILING(0)=0) - 如果档位边界是开区间(如“>200 才进下一档”),用
CEILING((price - 1) / 100.0)更安全,避免 price=200 时误进第 3 档
复杂阶梯必须搭配 CASE WHEN 或查找表,FLOOR/CEILING 仅作预处理
当档位边界不规则(如:[0,99]→0元优惠,[100,199]→5元,[200,499]→12元,[500+]→20元),硬套 FLOOR 会写出一堆 CASE WHEN FLOOR(price/100) = 0 THEN ... 这种脆弱逻辑——一旦加个 150 元档,整个表达式就得重写。
- 推荐做法:建一张
price_tiers表,字段为min_amount、max_amount、discount,然后JOIN或用(SELECT ... FROM tiers WHERE p.price BETWEEN min_amount AND max_amount LIMIT 1) - 若坚持用表达式,
FLOOR可简化边界计算,例如把原价归一化到「档位序号」:FLOOR(LEAST(GREATEST(price, 0), 999999) / 100.0)再映射,但仍不如CASE直观 - 性能上,单次查询用
CASE开销几乎为零;但若在WHERE中用FLOOR(price/100) = 2,可能使索引失效(除非你建了函数索引,如 PostgreSQL 的CREATE INDEX ON products (FLOOR(price/100.0)))
真正容易被忽略的是:阶梯计算往往要和税费、运费、会员等级叠加,而 FLOOR 和 CEILING 不关心上下文。它们只是数学操作符,不是业务规则引擎。别指望靠一个函数兜住所有价格策略。










