数据库 数据库中小数的字段类型慎用 float

samport · 2018年11月28日 · 最后由 sevk 回复于 2018年11月30日 · 9637 次阅读

最近在项目中碰到一个奇怪的现象:商品表中明明有价格为 72.05 元的商品,用"价格等于 72.05"的条件却检索不出来。

Product.where(price: 72.05).first

但是如果用区间条件的话,是能检索出来该商品的:

Product.where("price > ?", 72).where("price < ?", 73)

开始以为是 ruby 程序的问题,后来直接在 mysql 命令行下用 sql 语句去检索,问题一样存在,看来是数据库本身的问题。

反复检查以后,发现价格字段类型是 float 类型。使用 alter 的语句将价格字段修改成decimal (总位数,小数点后位数),问题得到了解决。

-- SQL 语句:
ALTER TABLE products MODIFY COLUMN price decimal(10,2);

当然,在 rails 项目里,按照流程还是必须创建和执行如下的 migration 的。不过不知道为什么这个操作并不能反映到 mysql 上,所以只能额外手工执行上面的 mysql 操作。

class ChangePirce < ActiveRecord::Migration[5.2]
  def change
      change_column : products, :price, :decimal, :precision => 10, :scale => 2
  end
end

关于 float 与 decimal 类型的区别,看起来不起眼,但是不注意的话会掉到这样的坑里去。这次的经验让我明白,以后跟金额相关的字段,统统明确用 decimal 类型。

跟钱有关的用 decimal 不是常识么

protip: 存 decimal,app 端比较用 to_d

关键字:浮点数、IEEE 754

确实跟自己经验不足有关。这种问题最坑人的是程序不会报错,只能在通过测试甚至上线后才能发现失误。

整数 + 币种可解决问题,也能解决多币种问题

钱,应该用整数

用 PG 就没这个问题,直接用 money 类型

确实是个坑,比较反直觉。感谢分享 :)

整数 + 千分比

单精度浮点数的有效位数是 7 位。 双精度浮点数的有效位数是 16 位。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号