40

我目前正在为一个小型金融相关项目学习和应用 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
4

7 回答 7

41

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

有关技术含量较低的介绍,请参阅舍入浮点数的麻烦;如果您想了解一下,请阅读每位计算机科学家应该了解的浮点运算知识

建议使用整数类型(以美分存储值)是为了避免潜在的舍入错误。这种方法在 MongoDB 文档中描述为“使用比例因子”来建模货币数据,并且是 MongoDB 3.2 和更早版本的一般解决方法。

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

于 2012-07-18T13:28:19.367 回答
6

MongoDb在 3.4 版本中增加了对 Decimal 数据类型的支持。它也可以从shell获得。

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

与仅存储十进制值的近似值的双精度数据类型不同,十进制数据类型存储精确值。例如,十进制 NumberDecimal("9.99") 的精确值为 9.99,而双精度 9.99 的近似值为 9.9900000000000002131628...

于 2016-12-06T02:05:48.833 回答
4

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

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

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

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

于 2013-11-18T09:29:27.280 回答
4

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

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

适用于模式对象,例如

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

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

于 2014-03-16T22:04:58.217 回答
4

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

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

于 2016-06-09T05:51:53.620 回答
3

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

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

{price: 9990, currency: "USD" }

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

于 2015-07-09T12:10:58.560 回答
3

NodeJS 用户:

请注意,如果使用 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) }
  }
)
于 2021-11-16T00:49:25.677 回答