PostgreSQL:货币应使用哪种数据类型?


128

好像Money类型如所描述的不鼓励这里

我的应用程序需要存储货币,我应该使用哪种数据类型?数字,金钱还是浮动?


7
如果您已经阅读了整个主题,那么数字就是您的最佳选择。
razpeitia

对于使用多种货币并关心存储除金额外的货币代码的任何人,您可能希望在数据库(SO)和ISO 4217(Wikipedia)中查看货币建模。简短的答案是,您将需要两列。
法比恩·斯瑙瓦特

Answers:


90

强制2单位精度的数字。切勿使用float或数据类型之类的float来表示货币,因为如果这样做,当财务报告的底线数字错误地加上+或-几美元时,人们将不高兴。

据我所知,货币类型只是出于历史原因而保留。


9
这就是为什么您避免使用浮点数的原因。如果使用任何精度不等于10的幂除以偶数,Numeric都会产生舍入错误。(无论如何,精度2都是一个坏主意……请检查文档。)
Doradus

6
如果要支持任意货币,请不要使小数位等于2(以PostgreSQL术语来说,精度是所有数字的数字,例如121.121中的精度等于6)。有货币,例如巴林第纳尔,其中1000个子单位等于一个单位,有些货币根本没有子单位。
Nikolay Arhipov '18

@NikolayArhipov好点,所以最大实际上是scale - precision
Konrad

1
numeric(3,2)将能够存储最大9.99 3-2 = 1
Konrad,

5
不要这样做!除非您打算在将任何值加载到其他语言之前再乘以100,然后再对整数进行数学运算,否则最终将得到错误的结果。用美分(您要处理的最小货币单位)存储事物,并为自己节省一些麻烦。在许多情况下,答案很差。
阿瓦曼德

111

您的消息来源绝不是官方的。追溯到2011年,我什至都不认识作者。如果货币类型被正式“劝阻”,则PostgreSQL会在手册中这样说- 事实并非如此

有关更多官方信息,请阅读pgsql-general中的此线程(仅从本周开始!),并请核心开发人员发表声明,包括D'Arcy JM Cain(货币类型的原始作者)和Tom Lane:

有关最新版本中的改进的相关答案(和评论!):

基本上,money有(非常有限的)用途。在Postgres的维基建议将在很大程度上避免它,除了那些狭义的案件。超过的优势numeric性能

decimal只是numericPostgres中的别名,并且广泛用于货币数据,是“任意精度”类型。手册

该类型numeric可以存储数量非常大的数字。特别建议在需要精确度的情况下存储货币金额和其他数量。

就个人而言,integer如果小数美分永远不会出现(通常在赚钱的地方),我喜欢将货币存储为代表美分的货币。这比任何其他提到的选项都更有效。


4
邮件列表上的一些讨论确实给人的印象是,至少不建议使用货币类型,例如:此处:postgresql.nabble.com/Money-type-todos-td1964190.html#a1964192加上公平的说:版本8.2的手册确实不推荐使用它:postgresql.org/docs/8.2/static/datatype-money.html
a_horse_with_no_name 2015年

11
@a_horse_with_no_name:您的链接是从2007年开始的一个线程,在该线程中,当前版本是8.2,而该money类型实际上已被弃用。解决了问题,并在更高版本中重新添加了该类型。我个人喜欢将货币存储为integer代表美分。
Erwin Brandstetter'3

1
Erwin,您可能仅从数据库角度来看就是正确的想法。但是,如果您将Postgresql + Java组合在一起,那根本不好(根据我的经验)。阅读您的评论后,我将MONEY用于大多数货币字段,现在我收到此Java异常:“ 发生SQLException:org.postgresql.util.PSQLException:类型为double的值错误:2,500.00 ”。我已经搜索过,没有找到好的解决方案,所以我现在很无聊,现在将它们全部更改为NUMERIC或DECIMAL !!!
MD

1
@MD:很抱歉听到这个消息,但是我显然没有为Java发言(我不能)。错误消息是奇怪的。“双”?千位分隔符也可能是一个问题。您可能要开始一个新的问题。
Erwin Brandstetter,2015年

2
@PirateApp:是的,我个人最喜欢。您可能已经错过了我回答的最后一句话。
Erwin Brandstetter

68

您的选择是:

  1. bigint:以美分存储金额。这就是EFTPOS事务使用的东西。
  2. decimal(12,2):将金额存储在正好两位小数的位置。这是大多数通用分类帐软件使用的。
  3. float:糟糕的主意-准确性不足。这是朴素的开发人员使用的。

选项2是最常见且最容易使用的选项。尽可能使精度最大(在我的示例中为12,意味着总共12位)。

请注意,如果将计算结果(例如,涉及汇率)的多个事务聚合为具有业务意义的单个值,则精度应更高以提供准确的宏值;考虑使用类似的东西,decimal(18, 8)这样总和是准确的,并且各个值可以四舍五入到精确度以进行显示。


10
如果您使用任何类型的反向税计算或外汇计算,则至少需要保留小数点后4位,否则您将丢失数据。所以numeric(15,4)还是numeric(15,6)一个好主意。
Petrus Theron

3
第四个选项-使用字符串并在宿主语言中使用等效的非有损十进制类型。
ioquatix

2
一个可缩放的整数怎么办?如果将其存储为10000045并具有1000倍的缩放系数,肯定会存储10000.045不会造成什么伤害?
PirateApp

26

我将所有货币字段保留为:

numeric(15,6)

拥有那么多小数位似乎太过分了,但是,即使有最小的机会,您将不得不处理多种货币,您将需要那么多的精度进行转换。无论我要向用户展示什么,我总是以美元存储。这样,考虑到当天的转换率,我可以轻松转换为任何其他货币。

如果您只做一种货币就做任何事情,最糟糕的是您浪费了一些空间来存储一些零。


6
由于缺少截断,因此存在错误结果的风险。如果非零值无意泄漏到剩余的小数位(例如,包含0.333333美元的价格字段),那么您可能会遇到这样的情况,系统显示某人购买了3件商品,每件$ 0.33,总计$ 1.00而不是$ 0.99。
彼得斯,2016年

1
Perteris,那么您有什么建议呢?无论您在四舍五入时投入多少精度都是一个问题。我只是没有找到更好的方法,即使这不是理想的方法。
Michael Collette

3
定点并在适当的地方截断。一旦达到“可存储的”货币价值(例如,提供给客户的价格),就应该采用适当的度量标准,在大多数情况下,在标准零售环境中,这将是整分钱。如果您有不同的业务需求(例如,每件大宗商品的价格),则可能会有不同的准确性设置,但是您必须将演示文稿与存储一起对待-如果您使用x小数显示钱数(反之亦然,例如,在整个千)则还必须将其与存储的是准确,无少,但也仅此而已。
彼得尼斯(Peteris),2016年

对于许多可能与零售相关的网站。我与之合作的主要项目可能需要一方用一种货币查看相同的成本,而用另一种货币查看客户的价格,而对于第三种供应商来说。
Michael Collette

19

使用存储为的64位整数 bigint

我建议使用微美元(或类似的主要货币)。微型意味着百万分之一,所以1个微型美元= 0.000001美元。

  • 易于使用并且与每种语言兼容。
  • 足够的精度来处理零点几分。
  • 仅适用于很小的单价(例如广告展示次数或API费用)。
  • 与字符串或数字相比,用于存储的数据大小更小。
  • 通过计算易于维护精度,并在最终输出中应用舍入。

1
这是最正确的答案,任何合理的软件都可以处理最小的货币单位。对整数进行所有数学运算意味着您不必处理任何特定于语言的浮点整形。
阿瓦曼德

1
也将使用它。谢谢

为什么numeric(15,6)在另一个答案中建议了这个?
Juliusz Gonera

@JuliuszGonera对于答案中列出的原因。整数较小,在任何地方都受支持,并且避免了所有数学截断问题。它基本上是使用数字,但是将小数点后移,因此您拥有一个更兼容的整数。
Mani Gandham

1
嗯,对,我错过了有关存储的部分。谢谢!不幸的是,关于“易于使用并与每种语言兼容”,JavaScript支持的整数最大为9007199254740991,比的最大值小1000倍bigint。有developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/…,但它的支持有限(目前)和警告(例如,进行货币换算时,您不能轻易将其乘以浮点数) 。鉴于您可以使用微美元将其存储在JS整数中的最大值为90亿美元,这在大多数情况下可能仍然不错。
Juliusz Gonera

3

使用BigInt存储的货币为代表的最小货币单位的货币价值正整数(如100美分存储$ 1.00 100存储¥100(日元,零小数的货币),这是条纹做什么-一个全球电子商务最重要的金融服务公司。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.