On this page本页内容
Applications that handle monetary data often require the ability to capture fractional units of currency and need to emulate decimal rounding with exact precision when performing arithmetic. 处理货币数据的应用程序通常需要捕获货币的小数单位,并且在执行算术时需要精确模拟小数舍入。The binary-based floating-point arithmetic used by many modern systems (i.e., float, double) is unable to represent exact decimal fractions and requires some degree of approximation making it unsuitable for monetary arithmetic. 许多现代系统(如浮点、双精度)使用的基于二进制的浮点算法无法表示精确的小数,需要一定程度的近似,因此不适用于货币算术。This constraint is an important consideration when modeling monetary data.在建模货币数据时,此约束是一个重要的考虑因素。
There are several approaches to modeling monetary data in MongoDB using the numeric and non-numeric models.在MongoDB中,有几种方法可以使用数字和非数字模型来建模货币数据。
The numeric model may be appropriate if you need to query the database for exact, mathematically valid matches or need to perform server-side arithmetic, e.g., 如果需要查询数据库以获得精确、数学上有效的匹配,或者需要执行服务器端算法,例如,$inc
, $mul
, and aggregation pipeline arithmetic.$inc
、$mul
和聚合管道算法,则数字模型可能是合适的。
The following approaches follow the numeric model:以下方法遵循数值模型:
long
BSON type) by multiplying by a power of 10 scale factor.If there is no need to perform server-side arithmetic on monetary data or if server-side approximations are sufficient, modeling monetary data using the non-numeric model may be suitable.如果不需要对货币数据执行服务器端运算,或者如果服务器端近似足够,则使用非数值模型对货币数据建模可能是合适的。
The following approach follows the non-numeric model:以下方法遵循非数值模型:
string
and another field stores a binary-based floating-point (double
BSON type) approximation of the value.string
,另一个字段存储该值的基于二进制的浮点(double
BSON类型)近似值。The decimal
BSON type uses the IEEE 754 decimal128 decimal-based floating-point numbering format. Unlike binary-based floating-point formats (i.e., the double
BSON type), decimal128 does not approximate decimal values and is able to provide the exact precision required for working with monetary data.decimal
BSON类型使用基于IEEE 754十进制128十进制的浮点编号格式。与基于二进制的浮点格式(即double
BSON类型)不同,decimal128
不近似十进制值,并且能够提供处理货币数据所需的精确精度。
From 在mongosh
decimal
values are assigned and queried using the NumberDecimal()
constructor. mongosh
中,使用NumberDecimal()
构造函数分配和查询decimal
值。The following example adds a document containing gas prices to a 以下示例将包含天然气价格的文档添加到gasprices
collection:gasprices
集合:
db.gasprices.insert{ "_id" : 1, "date" : ISODate(), "price" : NumberDecimal("2.099"), "station" : "Quikstop", "grade" : "regular" }
The following query matches the document above:以下查询与上面的文档匹配:
db.gasprices.find( { price: NumberDecimal("2.099") } )
For more information on the 有关decimal
type, see NumberDecimal.decimal
类型的详细信息,请参阅NumberDecimal。
A collection's values can be transformed to the 通过执行一次性转换或通过修改应用程序逻辑以在访问记录时执行转换,可以将集合的值转换为decimal
type by performing a one-time transformation or by modifying application logic to perform the transformation as it accesses records.decimal
类型。
Alternative to the procedure outlined below, starting in version 4.0, you can use the 除了下面概述的过程之外,从版本4.0开始,您可以使用$convert
and its helper $toDecimal
operator to convert values to NumberDecimal()
.$convert
及其助手$toDecimal
运算符将值转换为NumberDecimal()
。
A collection can be transformed by iterating over all documents in the collection, converting the monetary value to the 可以通过迭代集合中的所有文档,将货币值转换为decimal
type, and writing the document back to the collection.decimal
类型,然后将文档写回集合来转换集合。
It is strongly advised to add the 强烈建议将decimal
value to the document as a new field and remove the old field later once the new field's values have been verified.decimal
值作为新字段添加到文档中,并在验证新字段的值后删除旧字段。
Be sure to test 确保在独立的测试环境中测试decimal
conversions in an isolated test environment. decimal
转换。Once datafiles are created or modified with MongoDB version 3.4 they will no longer be compatible with previous versions and there is no support for downgrading datafiles containing decimals.使用MongoDB 3.4版创建或修改数据文件后,它们将不再与以前的版本兼容,并且不支持降级包含小数的数据文件。
Scale Factor Transformation:比例因子转换:
Consider the following collection which used the Scale Factor approach and saved the monetary value as a 64-bit integer representing the number of cents:考虑以下集合,该集合使用比例因子方法,并将货币值保存为表示美分数的64位整数:
{ "_id" : 1, "description" : "T-Shirt", "size" : "M", "price" : NumberLong("1999") }, { "_id" : 2, "description" : "Jeans", "size" : "36", "price" : NumberLong("3999") }, { "_id" : 3, "description" : "Shorts", "size" : "32", "price" : NumberLong("2999") }, { "_id" : 4, "description" : "Cool T-Shirt", "size" : "L", "price" : NumberLong("2495") }, { "_id" : 5, "description" : "Designer Jeans", "size" : "30", "price" : NumberLong("8000") }
The 通过使用long
value can be converted to an appropriately formatted decimal
value by multiplying price
and NumberDecimal("0.01")
using the $multiply
operator. $multiply
运算符将price
和NumberDecimal("0.01")
相乘,可以将long
值转换为适当格式的十进制值。The following aggregation pipeline assigns the converted value to the new 以下聚合管道将转换后的值分配给priceDec
field in the $addFields
stage:$addFields
阶段中的新priceDec
字段:
db.clothes.aggregate( [ { $match: { price: { $type: "long" }, priceDec: { $exists: 0 } } }, { $addFields: { priceDec: { $multiply: [ "$price", NumberDecimal( "0.01" ) ] } } } ] ).forEach( ( function( doc ) { db.clothes.save( doc ); } ) )
The results of the aggregation pipeline can be verified using the 聚合管道的结果可以使用db.clothes.find()
query:db.clothes.find()
查询进行验证:
{ "_id" : 1, "description" : "T-Shirt", "size" : "M", "price" : NumberLong(1999), "priceDec" : NumberDecimal("19.99") } { "_id" : 2, "description" : "Jeans", "size" : "36", "price" : NumberLong(3999), "priceDec" : NumberDecimal("39.99") } { "_id" : 3, "description" : "Shorts", "size" : "32", "price" : NumberLong(2999), "priceDec" : NumberDecimal("29.99") } { "_id" : 4, "description" : "Cool T-Shirt", "size" : "L", "price" : NumberLong(2495), "priceDec" : NumberDecimal("24.95") } { "_id" : 5, "description" : "Designer Jeans", "size" : "30", "price" : NumberLong(8000), "priceDec" : NumberDecimal("80.00") }
If you do not want to add a new field with the 如果不想添加具有decimal
value, the original field can be overwritten. decimal
值的新字段,则可以覆盖原始字段。The following 下面的updateMany()
method first checks that price
exists and that it is a long
, then transforms the long
value to decimal
and stores it in the price
field:updateMany()
方法首先检查price
是否存在以及它是否为long
,然后将long
值转换为decimal
并将其存储在price
字段中:
db.clothes.updateMany( { price: { $type: "long" } }, { $mul: { price: NumberDecimal( "0.01" ) } } )
The results can be verified using the 可以使用db.clothes.find()
query:db.clothes.find()
查询验证结果:
{ "_id" : 1, "description" : "T-Shirt", "size" : "M", "price" : NumberDecimal("19.99") } { "_id" : 2, "description" : "Jeans", "size" : "36", "price" : NumberDecimal("39.99") } { "_id" : 3, "description" : "Shorts", "size" : "32", "price" : NumberDecimal("29.99") } { "_id" : 4, "description" : "Cool T-Shirt", "size" : "L", "price" : NumberDecimal("24.95") } { "_id" : 5, "description" : "Designer Jeans", "size" : "30", "price" : NumberDecimal("80.00") }
Non-Numeric Transformation:非数值转换:
Consider the following collection which used the non-numeric model and saved the monetary value as a 考虑以下集合,该集合使用非数字模型,并将货币值保存为具有值的精确表示形式的string
with the exact representation of the value:string
:
{ "_id" : 1, "description" : "T-Shirt", "size" : "M", "price" : "19.99" } { "_id" : 2, "description" : "Jeans", "size" : "36", "price" : "39.99" } { "_id" : 3, "description" : "Shorts", "size" : "32", "price" : "29.99" } { "_id" : 4, "description" : "Cool T-Shirt", "size" : "L", "price" : "24.95" } { "_id" : 5, "description" : "Designer Jeans", "size" : "30", "price" : "80.00" }
The following function first checks that 下面的函数首先检查price
exists and that it is a string
, then transforms the string
value to a decimal
value and stores it in the priceDec
field:price
是否存在以及它是否为string
,然后将string
值转换为decimal
值并将其存储在priceDec
字段中:
db.clothes.find( { $and : [ { price: { $exists: true } }, { price: { $type: "string" } } ] } ).forEach( function( doc ) { doc.priceDec = NumberDecimal( doc.price ); db.clothes.save( doc ); } );
The function does not output anything to the command line. 该函数不向命令行输出任何内容。The results can be verified using the 可以使用db.clothes.find()
query:db.clothes.find()
查询验证结果:
{ "_id" : 1, "description" : "T-Shirt", "size" : "M", "price" : "19.99", "priceDec" : NumberDecimal("19.99") } { "_id" : 2, "description" : "Jeans", "size" : "36", "price" : "39.99", "priceDec" : NumberDecimal("39.99") } { "_id" : 3, "description" : "Shorts", "size" : "32", "price" : "29.99", "priceDec" : NumberDecimal("29.99") } { "_id" : 4, "description" : "Cool T-Shirt", "size" : "L", "price" : "24.95", "priceDec" : NumberDecimal("24.95") } { "_id" : 5, "description" : "Designer Jeans", "size" : "30", "price" : "80.00", "priceDec" : NumberDecimal("80.00") }
It is possible to perform the transformation to the 可以从应用程序逻辑中执行到decimal
type from within the application logic. decimal
类型的转换。In this scenario the application modified to perform the transformation as it accesses records.在这种情况下,应用程序修改为在访问记录时执行转换。
The typical application logic is as follows:典型的应用逻辑如下:
decimal
typedecimal
类型If the new 如果新的decimal
field does not exist:decimal
字段不存在:
If you are using MongoDB version 3.4 or higher, using the decimal type for modeling monetary data is preferable to the Scale Factor method.如果您使用的是MongoDB 3.4版或更高版本,则使用decimal
类型建模货币数据比使用比例因子方法更可取。
To model monetary data using the scale factor approach:使用比例因子方法对货币数据建模:
USD
currency.USD
表示的货币值的精度低至1美分的十分之一。For example, the following scales 例如,下面的比例为9.99 USD
by 1000 to preserve precision up to one tenth of a cent.9.99 USD
乘以1000,以保持高达十分之一美分的精度。
{ price: 9990, currency: "USD" }
The model assumes that for a given currency value:该模型假设对于给定货币值:
When using this model, applications must be consistent in performing the appropriate scaling of the values.使用此模型时,应用程序在执行适当的值缩放时必须保持一致。
For use cases of this model, see Numeric Model.有关此模型的用例,请参阅数字模型。
To model monetary data using the non-numeric model, store the value in two fields:要使用非数字模型对货币数据建模,请将值存储在两个字段中:
BinData
or a string
.BinData
或string
。The following example uses the non-numeric model to store 以下示例使用非数字模型存储9.99 USD
for the price and 0.25 USD
for the fee:9.99 USD
的价格和0.25 USD
的费用:
{ price: { display: "9.99", approx: 9.9900000000000002, currency: "USD" }, fee: { display: "0.25", approx: 0.2499999999999999, currency: "USD" } }
With some care, applications can perform range and sort queries on the field with the numeric approximation. 在一定程度上,应用程序可以使用数值近似值对字段执行范围和排序查询。However, the use of the approximation field for the query and sort operations requires that applications perform client-side post-processing to decode the non-numeric representation of the exact value and then filter out the returned documents based on the exact monetary value.然而,使用近似字段进行查询和排序操作需要应用程序执行客户端后处理,以解码精确值的非数字表示,然后根据精确货币值筛选出返回的文档。
For use cases of this model, see Non-Numeric Model.有关此模型的用例,请参阅非数值模型。