【问题标题】:MongoDB - What about Decimal type of value?MongoDB - 十进制类型的值怎么样?
【发布时间】:2012-07-17 12:31:37
【问题描述】:

我目前正在学习 MongoDB 并将其应用于一个小型金融相关项目。


当我阅读MongoDB in Action 时,它说:

BSON 数字类型通常出现的唯一其他问题是 缺乏小数支持。这意味着如果您计划 在MongoDB中存储货币值,需要使用整数类型 并保持以美分为单位。


我的金融相关产品会涉及一些货币价值,但我对上述说法有点困惑或担心。以下是我的问题:

  1. 我可以在我的项目中将double 用于那些currency values 吗?
  2. 如果我直接为他们使用double会发生什么或后果?
  3. 如果十进制类型是金融产品的必备品,那么使用 MongoDB 是不是一个坏主意?
  4. you need to use an integer type and keep the values in cents 是什么意思?这是否意味着如果我要存储1.34 dollars,那么我应该存储134 cents

【问题讨论】:

标签: mongodb decimal bigdecimal bson


【解决方案1】:

如果出于财务目的需要精确表示,则不适合使用双精度值或浮点值,因为小数部分会出现舍入误差。某些十进制值不能使用基于二进制的浮点数表示,必须近似。

有关技术含量较低的介绍,请参阅The trouble with rounding floating point numbers;如果您想了解一下,请阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic

建议使用整数类型(以美分存储值)以避免潜在的舍入错误。这种方法在 modelling monetary data 的 MongoDB 文档中被描述为“Using a Scale Factor”,是 MongoDB 3.2 及更早版本的通用解决方法。

MongoDB 3.4 包含一个新的Decimal BSON type,它为操纵货币数据字段提供了精确的精度。

【讨论】:

  • 你能给我举一个关于 MongoDB 中 using an integer type (storing the value in cents) 的例子吗?
  • @JacksonTale:Kyle 的博客上有一个例子:MongoDB and E-commerce。这已经有几年的历史了(提到 MongoDB 1.5 开发版本;当前的产品版本是 2.0,开发版本是 2.1),但适用一般模式概念。从那以后,MongoDB 有了很多改进,比如更好的replication、默认启用的journaling,以及即将推出的aggregation framework
  • 在电子商务或金融应用程序中支持小数非常很重要。使用整数充其量只是一种技巧。不同的货币使用不同的小数位数,因此不可能只假设您可以使用最后 2 或 4 位数字表示小数的整数。如有疑问,请在 Google 上搜索 Knight Capital,该公司因整数表示的错误在 45 分钟内损失了 4.4 亿美元
  • “原生的 Decimal 类型可以处理小数点的额外格式,但不会对您决定使用哪个数据库产生重大影响”。认真的吗?
【解决方案2】:

当您不想将货币存储为分值时,您可以将 1.34 美元的货币存储为如下对象:

{
    major: 1,
    minor: 34,
    currency: "USD"
}

用这样的对象做任何数学运算都不容易,也不会使用商业四舍五入规则。但是你不应该在数据库上做任何业务逻辑,尤其是当它是一个像 MongoDB 这样的“哑”数据库时。

您应该做的是在您的应用程序中将这些对象从/到Money 类序列化/反序列化,该类实现尊重舍入规则的基本货币数学运算,并在您尝试使用不同货币进行操作时抛出异常单位($12.34 + 14.95€ = error - 必须首先通过提供汇率将一种货币转换为另一种货币)。

【讨论】:

  • 特别是在像“促销产品”这样的分支中,您会遇到每件商品 0.00005 美元的金额。然后“以美分为单位保持价值”不成立。然后,您需要一些数据类型,例如 MySQL 中的 DECIMAL(12,6),或者像 Philipp 那样拆分主要和次要部分,并添加第三项“除数”,允许您指定“次要”部分中有零。另一个选项是 {"amount":123305,"precision":4},表示值为 12.3305。您只需将金额除以 10^4
【解决方案3】:

如果您使用的是 Mongoose,那么您可以在架构定义中使用 getter/setter 函数,例如

function getDecimalNumber(val) {    return (val/1000000); }
function setDecimalNumber(val) {    return (val*1000000); }

适用于架构对象如

balance: { type: Number, default: 0, get: getDecimalNumber, set: setDecimalNumber },

要乘/除的零的数量取决于您想要的精度。

【讨论】:

  • return (val/1000000).toFixed(2);
【解决方案4】:

我知道这篇文章很旧,但它在 Google 上的排名很高,所以...

存储财务数据的最佳解决方案是使用 MongoDB 自己在此处 http://docs.mongodb.org/v2.6/tutorial/model-monetary-data/#monetary-value-exact-precision 记录的精确精度。

{price: 9990, currency: "USD" }

假设您需要 2 位精度,当您需要数据时只需除以 100。缺点是您始终需要以相同的精度工作。

【讨论】:

  • 不是什么大不了的事,但 Mongo 在技术上提到了 1000 倍缩放。显然,您可以随意缩放,因为它是一致的。
【解决方案5】:

看起来 MongoDB 终于添加了对小数的支持,虽然在撰写本文时这刚刚完成开发,但希望它应该很快在稳定版本(3.4?)中可用。

https://jira.mongodb.org/browse/SERVER-1393

【讨论】:

    【解决方案6】:

    MongoDb 增加了对Decimal data type in 3.4 version 的支持。也可以从shell 获得。

    3.4 添加了对使用新的十进制数据类型的十进制128 格式的支持。 decimal128 格式支持最多 34 位小数的数字 数字(即有效数字)和 -6143 到的指数范围 +6144。

    不像双精度数据类型,它只存储一个近似值 十进制值,十进制数据类型存储精确值。为了 例如,十进制 NumberDecimal("9.99") 的精确值为 9.99 其中双 9.99 的近似值为 9.9900000000000002131628...

    【讨论】:

      【解决方案7】:

      请注意,如果使用 NodeJS,请不要直接使用 NumberDecimal(string),而是使用 Decimal128.fromString(string)。

      参考资料:

      例子:

      const Decimal128 = require('mongodb').Decimal128
      
      usersCollection.findOneAndUpdate({_id: ObjectID(this.data.userId), "myWells.wellId": ObjectID(this.data.wellId)},
        { 
          $set: { "myWells.$.interest": Decimal128.fromString(this.data.interest) }
        }
      )
      

      【讨论】:

        猜你喜欢
        • 2022-11-25
        • 1970-01-01
        • 1970-01-01
        • 2016-09-21
        • 2011-03-15
        • 1970-01-01
        • 1970-01-01
        • 2020-08-10
        • 2020-10-31
        相关资源
        最近更新 更多