在MySQL中存储货币值的最佳数据类型是什么?

357

我想在MySQL数据库中存储许多记录。它们都包含货币值。但是我不知道每个值会插入多少位数。
为此,我应该使用哪种数据类型?
VARCHAR还是INT(或其他数字数据类型)?


21
“deimal(10,2)”是我使用的代码…您可以根据预期大小调整数值。 - Manse
9
相关问题是最佳货币数据类型 ;). - shA.t
14个回答

488

由于货币需要精确的表示,因此不要使用仅近似的数据类型,如float。您可以使用固定点数值数据类型。

decimal(15,2)
  • 15 是精度(值的总长度,包括小数位)
  • 2 是小数点后的位数

请参考MySQL 数字类型

这些类型用于在处理货币等数据时需要保留精确的精度。


3
在这种情况下,十进制和数值数据类型之间可能有什么区别? - Emilio Gort
65
在MySQL中,decimalnumeric是相同的。 - juergen d
32
我个人在财务记录中使用 numeric(19,4),这样可以更灵活地应对新的需求。 - YahyaE
12
我同意 YahyaE 的观点,更多的小数位更好。有一些货币通常使用 3 个小数位,比如巴林第纳尔、约旦第纳尔或科威特第纳尔,因此您至少需要三个小数位。四或五个小数位会更好。 - Edwin Hoogerbeets
3
我不是会计师,但在英国经营小企业时,我记得很久以前读过一篇文章,提到即使对于英镑、美元等货币,货币数字也应该保留4位小数,这样某些计算可能会在某些晦涩的会计背景下实际使用最后两位小数。需要会计师确认/驳斥。 - mike rodent
显示剩余2条评论

94

您可以使用DECIMALNUMERIC,它们都是相同的。

DECIMALNUMERIC类型存储精确的数值数据。当保留精确度很重要时,例如在货币数据中,使用这些类型。在MySQL中,NUMERIC被实现为DECIMAL,因此关于DECIMAL的以下备注同样适用于NUMERICMySQL

DECIMAL(10,2)

示例设置

好文推荐


我在使用decimal(10,2)来表示我的货币值,但是当我输入类似于867,000.00这样的数值时,它只保存了867。我做错了什么? - codeinprogress
@codeinprogress,你的金钱数值中有一个逗号...如果以同样的方式输入数据库,Mysql将从逗号位置开始截断。 - Chidozie Duru

41

我更喜欢使用 BIGINT,并通过 乘以 100 将值存储,使其成为整数。

例如,要表示货币价值 93.49,则将其存储为9349,在显示该值时,我们可以 除以 100 并显示。这将占用更少的存储空间。

注意:
大多数情况下,我们不进行 货币 * 货币 的乘法运算,但如果确实需要进行此操作,则应将结果除以 100 并存储,以便返回正确的精度。


21
相较于DECIMAL,有什么优势呢?使用它会需要将分转换为元,如果忘记了就会出现问题。 - user565869
1
空间是唯一的优势,但是当我们使用这个功能时需要更加小心。 - Dinesh P.R.
4
如果您存储的金额是分数美分(例如“$0.005”或“$0.12345”),请小心使用刻度删除方法,因为在乘以100后它们不会缩减为整数。如果您知道值的精度,最好的选择显然是使用DECIMAL。但是,如果您不了解精度(就像我的例子一样),那么使用FLOAT是否合适? - Quinn Comendant
3
使用这种方法的优点在于,当使用像JavaScript这样使用IEEE-754来存储浮点数的语言时。这个规范不能保证0.1 + 0.2 === 0.3是正确的。将货币存储为整数可以确保您的应用程序不会遭受此类错误。尽管如此,这可能不是最佳解决方案。我在研究解决方案时找到了这个页面,但我还没有完成。 - Gary Ott
我喜欢这种方法,但有时候,你所工作的系统并不需要如此精确,因此为了提高生产力,我经常使用decimal(10,2)。 - Ricardo Vigatti
显示剩余4条评论

28

这取决于您的需求。

通常使用 DECIMAL(10,2) 就足够了,但如果您需要更精确的值,可以设置为 DECIMAL(10,4)

如果你需要处理大数值,请将 10 替换为 19


我在使用decimal(10,2)来表示我的货币值,但是当我输入类似于867,000.00这样的数值时,它只保存了867。我做错了什么? - codeinprogress
2
@codeinprogress 是否使用了错误的区域设置或小数分隔符? - David Balažic
1
@codeinprogress,你正在使用“,”来进行数字分组。请不要这样做。永远不要使用逗号或点来进行数字分组。 - 12431234123412341234123

18

如果您的应用程序需要处理高达一万亿的货币价值,那么这应该可以胜任:13.2。

如果您需要遵守GAAP(普遍公认会计原则),则应使用:13.4。

通常情况下,您应在将输出舍入为13.2之前将货币价值相加至13.4。


6
如果您要使用比特币,您需要使用8个小数位,尽管大多数钱包只支持到mBTC(即3个小数位)。 http://en.wikipedia.org/wiki/Bitcoin - Christian
不要认为这个答案是正确的。@david.ee有相关来源吗? - Evan Carroll
@EvanCarroll 让我回答david.ee的问题。我认为这篇文章可能是来源 https://rietta.com/blog/2012/03/03/best-data-types-for-currencymoney-in/。 - naXa stands with Ukraine
@naXa,该链接并没有引用任何支持使用13.4进行GAAP的声明的来源。你所做的只是链接到一篇提出同样未经证实的声明的文章。 - iheanyi

13

我们使用double

*喘气*

为什么?

因为它可以表示任何15位数字,而不受小数点位置的限制。只需8个字节!

这样可以表示:

  • 0.123456789012345
  • 123456789012345.0

......以及它们之间的任何值。

这很有用,因为我们正在处理全球货币,而double可以存储我们可能遇到的各种小数位数。

一个单一的double字段可以表示999,999,999,999,999日元,9,999,999,999,999.99美元甚至是9,999,999.99999999比特币。

如果你尝试使用decimal,你需要decimal(30, 15),这将花费14个字节。

注意事项

当然,使用double并非没有注意事项。

然而,它不会失去精度,正如某些人所指出的那样。尽管double本身可能不是基于10进制的内部精确表示,但我们可以通过将从数据库中提取的值舍入到其有效小数位数来使其精确。如果需要的话。(例如,如果要输出并且需要基于10进制表示法。)

注意事项在于,每次对它执行算术运算时,我们需要在:

  1. 对其进行比较之前规范化结果(将其舍入到其有效小数位数)。
  2. 写回到数据库之前规范化结果。

另外一个需要注意的地方是,与 decimal(m, d) 不同的是,数据库无法防止程序插入超过 m 个数字的数字。而对于 double 类型则不存在这种验证。程序可能会插入一个20位数的用户输入值,然后它就会以不准确的金额被默默地记录下来。


@ChristofferHammarström 2.9699999999999998 远远超过 15 位数字的精度。你需要知道你的货币有多少个有效数字并将其舍入到该数字。如果是满 15 位,那么它将是 2.97000000000000,但你从 0.99 开始,所以可能是两位,因此是 2.97。详情请参见注意事项。 - antak
@ChristofferHammarström 2.9699999999999998远超过15位数字。您需要知道您的货币有多少个有效数字,并对其进行四舍五入。如果是15位有效数字,结果将为2.97000000000000,但您从0.99开始,所以可能只有两位有效数字,即2.97。详细内容请参阅注意事项。 - antak
所以你可以记得始终四舍五入并希望你没有足够的复合误差,或者你可以使用一个没有问题的数据类型。 - Christoffer Hammarström
@ChristofferHammarström 任何15位数字都可以用64位浮点数精确表示。进行算术运算,你仍然会在这15个数字内得到准确的结果。我不确定你想要打破这个的复利误差是什么。如果算术运算的结果需要超过15位数字,那么它将会出错,就像我不能将16位数字放入 decimal(15) 中一样。你不需要在算术运算期间四舍五入,但是在控制台上看到 2.9699999999999998 这样的数字是浮点数的特性,因为字符串化程序不知道你想要多少位数字。 - antak
@ChristofferHammarström 任何一个15位数都可以用64位浮点数精确表示。进行算术运算,结果仍将在这15位数内准确。我不确定你在想着哪些会破坏这一点的复合误差。如果算术运算的结果需要超过15位数字,那么它就会失效,就像我不能把一个16位数字放入decimal(15)中一样。在算术运算过程中不需要四舍五入,但是在控制台上打印出'2.9699999999999998'这样的字符串是浮点数的特性,因为字符串化程序无法知道你想要多少位数。 - antak
显示剩余4条评论

12

当提出这个问题时,没有人考虑比特币的价格。对于BTC来说,使用DECIMAL(15,2)可能是不够的。如果比特币价格上涨到10万美元或更高,我们需要至少DECIMAL(18,9)来支持我们应用程序中的加密货币。

DECIMAL(18,9)在MySQL中占用8个字节的空间(每9位数字占用4个字节)。


比特币可以被分成8个小数位。因此,0.00000001 BTC是可以在交易中处理的最小金额。我想你是指8而不是9吧? - Melroy van den Berg
3
我知道,但是9所占用的磁盘空间和8一样。根据MySQL文档:“DECIMAL列的值使用二进制格式存储,将九位小数压缩成4个字节。” - bizwiz
抱歉,现在我明白你的意思了。谢谢。 - Melroy van den Berg
1
实际上,DECIMAL(18,9) 占用 8 个字节,而不是 12 个字节。请参见 此处 - at54321
是的,你说得对 @at54321 - bizwiz

8

5

实际上,这取决于程序员的喜好。我个人使用:numeric(15,4)来符合 通用会计准则(GAAP


13
这与“程序员的偏好”或您个人使用的内容完全无关。它是由问题域所规定的,需要使用十进制。这不是程序员可以行使个人偏好的问题。 - user207421

4

尝试使用

Decimal(19,4)

通常这也适用于其他数据库。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接