新手问题 为什么要金额使用分为单位保存成整形在数据库中?

jasl · 2013年12月28日 · 最后由 jasl 回复于 2013年12月31日 · 17100 次阅读

找了很多资料都没有能解释清楚两者优缺点

记得 19wu 使用分为单位来保存金额,pq 应该有 decimal 类型的,并且可以直接映射到 Ruby 的 BigDecimal,可以说非常安全,为什么当初没采用呢? @doitian @saberma

PS:在 mongodb 中只能选择存分的形式,因为没有 Decimal 或者类似的数据类型,Mongoid 会将 BigDecimal 转换成 String 保存,这将完全丧失查询能力,同时当前版本 Mongoid 的 inc 原子操作实现有 bug,导致无法使用

我们是用的 decimal

参考: http://ruby-china.org/topics/6982 http://ruby-china.org/topics/7376

用 BigDecimal 是安全的,但是,太麻烦了,还是用分直接一点,计算结算全用分,仅显示的时候格式化为元。

不知道是不是和Money用 cents 有关 它的解释是 Represents monetary values as integers, in cents. This avoids floating point rounding errors.

#2 楼 @hisea 用 decemal 不会出现浮点数失精的问题,而且会 ar 会正确映射到 BigDecemal,所以计算也是安全的

#3 楼 @zj0713001 BigDecimal 计算和普通数字类型 FIxnum 还有 Float 都没啥差别吧

#3 楼 @zj0713001 你可能会这么做

def price
    self.price_in_cents / 100.0
end

但用 price 去计算是一定会发生的,如果是 Float,那么麻烦依然存在。所以,最终 Ruby 这边还是不得不去用 BigDecimal

#9 楼 @jasl 不用 / 100,可以只用来显示的 helper 用 %100

好吧,我没仔细看原帖,我能想到的区别: 1,内存跟储存的区别,两者是否占有同样大小空间? 2,运算的效能,bigdecemal 是比 float 慢的,integer 比 float 要快的, 3,fixnum 在 ruby 里面是 inmutable,跟 symbol 一样,所有的 5 都是同一个对象,BigDecimal 不是。 4,储存选择的灵活性跟通用性。

如果以上都不是考虑因素,那就无所谓了。

我们用的 postgres + decimal。Ruby 里直接用 BigDecimal。Ruby 2.1 开始可以用 decimal literal:0.1r

用 cents 保存的估计都不是金融系统(或是程序员懒惰),否则的话是没有办法处理一些金融方面的业务的,比如:http://en.wikipedia.org/wiki/Mill_%28currency%29

Mongo 没有 decimal?啊哈哈,估计也没太大关系,对数据精确度有要求的系统都不可能采用 Mongo 啊,LOL :trollface:

#11 楼 @hisea http://stackoverflow.com/questions/18934774/what-are-the-downsides-to-storing-money-values-as-cents-minor-units 等等还有一堆其他的讨论,浏览器历史记录没了...但是没有一个明显的讨论能得出“最佳实践”

#12 楼 @fredwu 我认为用 cents 是一种 trick,另外这个 cents 是指代系统里金额的最小单位

不太理解为什么说存分是懒惰,毕竟会增加代码量,实际是复杂了...而且看了一些讨论之后,完全糊涂为什么不直接用 decimal...

#12 楼 @fredwu 或者说所有的系统都支持 integer,那么用 integer 是最保守和最通用的解决方案

我们这的新人培训之一就是,遇到金额的数据库字段类型一定要用 BigDecimal,因为它有方法直接进行“银行家舍入法”,并且要是进行其他的舍入方法也能很方便的实现

PS:19wu 的里面我本来提了个金额用 BigDecimal 来储存的 issue,结果没被采用

#15 楼 @jasl 在 java 里面的用 integer 存储金额的话,连 13 个 9 的那么一个金额都没法保存的,还是用 BigDecimal 的好

我查了下,可能是因为老版本的 MySQL(4.1 及以下)的 decimal data type 是将数据保存为 string:

In MySQL versions up to and including 4.1, DECIMAL columns are represented as strings

http://dev.mysql.com/doc/refman/4.1/en/storage-requirements.html

#14 楼 @jasl 即使是最小单位,当计算 currency conversion 的时候,还是会不够用的。;)

@jasl 没想这么多,两个方案选了一个

https://github.com/19wu/19wu/issues/474

@jasl @doitian 我把原贴讨论的翻出来了,两种都能满足要求。

#18 楼 @fredwu #20 楼 @doitian #21 楼 @saberma

哦了 看来存分是一种 trick

我公司的使用方式简单直接 所有商品最低价格都是元 所以不存在这么难过的讨论 我在其他地方使用分做最低单位 然后显示的时候用 helper 做的 format...就是这样...

#23 楼 @zj0713001 其实我们的定价也都是元... 支付宝的接口金额那块是 decimal 的字符串,不过财付通是分

为啥会出现分的方案让我很好奇,但是查了一些资料后没找到什么信息

#24 楼 @jasl 其实源于很久以前没有 decimal 解决方案的时候 然后口口相传就成了这样的约定大于配置 其实怎么都行... 只要是确保金额安全 项目统一即可

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