使用 Java 在 PostgreSQL 上的货币数据
Posted
技术标签:
【中文标题】使用 Java 在 PostgreSQL 上的货币数据【英文标题】:Money data on PostgreSQL using Java 【发布时间】:2013-08-12 17:45:06 【问题描述】:我正在编写一个挖掘货币兑换数据的 Java 程序。数据可以有多个十进制数字,例如“0.973047”。经过一番研究,我发现 BigDecimal 是 Java 的正确数据类型,但我应该为 PostgreSQL 使用哪种数据类型?
【问题讨论】:
【参考方案1】:
NUMERIC
/DECIMAL
作为Joachim Isaksson said,您想使用NUMERIC
/DECIMAL
类型,作为任意精度类型。
关于NUMERIC
/DECIMAL
的两个重点:
NUMERIC
和 DECIMAL
的 SQL 类型 接近但不相同。在SQL:92 中,遵循您为NUMERIC
指定的精度,而对于DECIMAL
,允许数据库服务器添加超出您指定的额外精度。这里 Postgres 再次偏离标准,NUMERIC
和 DECIMAL
documented 是等效的。
条款:
精度是数字中的总位数。 Scale 是小数点右边的位数(小数部分)。 ( Precision - Scale ) = 小数点左边的位数(整数部分)。明确项目的精度和规模规格:
大精度必须足够大,以处理未来可能需要的更大数字。这意味着......也许您的应用今天的工作量为数千美元,但将来必须执行汇总报告,最终达到数百万美元。 小出于某些会计目的,您可能需要存储最小货币金额的一小部分。意思是……超过 3 或 4 位小数,而不是 USD 中的一分钱所需的 2 位。避免MONEY
类型
Postgres 也提供MONEY
类型。这听起来可能是对的,但对于大多数用途来说可能不是最好的。一个缺点是MONEY
的规模由基于locale 的数据库范围的配置设置 设置。因此,当您切换服务器或进行其他更改时,该设置很容易发生危险的变化。此外,您无法控制特定列的设置,但您可以在NUMERIC
类型的每一列上设置比例。最后,MONEY
不是标准 SQL,如 list of standard SQL data types 所示。 Postgres 包含MONEY
以方便人们从其他数据库系统移植数据。
移动小数点
有些人采用的另一种选择是移动小数点,并以大整数数据类型存储。
例如,如果将USD 美元存储为一美分,则将任何给定的小数乘以 100,然后转换为整数类型,然后继续。例如,$123.45 变成整数 12,345。
这种方法的好处是执行时间更快。对整数执行诸如sum
之类的操作非常快。整数的另一个好处是更少的内存使用。
我发现这种方法很烦人、令人困惑且有风险。烦人,因为计算机应该为我们工作,而不是反对我们。有风险,因为某些程序员或用户可能会忽略乘法/除法以转换回小数,从而给出不正确的结果。如果在没有对精确小数的良好支持的系统中工作,这种方法可能是一种可接受的解决方法。
当我们在 SQL 中使用 DECIMAL
/NUMERIC
在 Java 中使用 BigDecimal
时,我认为移动小数点没有任何好处。
四舍五入和NaN
在您的应用程序编程以及在 Postgres 服务器端进行的任何计算中,要非常小心并注意小数部分的舍入和截断。并测试无意中弹出的NaNs。
在应用程序和 Postgres 双方中,始终避免使用 floating point 数据类型来赚钱。 浮点是为性能速度而设计的,但以准确性为代价。计算可能会导致小数部分出现看似疯狂的额外数字。不适合财务/金钱或其他需要准确性的目的。
BigDecimal
是的,在 Java 中,您希望 BigDecimal
作为您的任意精度类型。 BigDecimal
速度较慢并使用更多内存,但会准确存储您的金额。 SQL NUMERIC
/DECIMAL
应该映射到BigDecimal
,正如在here 和*** 上讨论的那样。
BigDecimal
是 Java 最棒的地方之一。我不知道有任何其他平台具有类似的类别,尤其是一个实施良好且经过多年改进和修复的平台。
使用BigDecimal
肯定比使用Java’s floating-point types、float
和double
慢。但在现实世界的应用程序中,我怀疑你的金钱计算会成为任何瓶颈。此外,您或您的客户想要哪一种:最快金钱计算,还是准确金钱计算? ?
我一直认为BigDecimal
是 Java 中最大的沉睡特性,与许多其他缺乏对小数的复杂支持的平台相比,使用 Java 平台的最重要优势。
类似问题:Best Data Type For Currency
【讨论】:
使用Money
的另一个问题是,如果它是根据服务器的语言环境设置的,是否会使处理多种货币变得困难。或者,如果服务器时钟已设置为 UTC/GMT....
numeric
与 integer
(和“移动小数点”)的一个缺点在 the PostgreSQL documentation (version 9.1) 中提到——“与整数类型相比,数值的算术非常慢,或者到浮点类型”。
@KennyEvitt 是的,这就是浮点的全部意义(看看我在那里做了什么?)......速度。浮点牺牲精度以获得更快的性能。但是,现代计算机每秒执行几 十亿 次计算,您应该在通过破解数据类型过早优化之前三思而后行。在您确定(可测量的、凭经验的)您有问题之前,请在您的数据类型中使用适当的拟合。
嗨@basil-bourque。你说,“仔细阅读文档以了解你应该指定比例以避免默认比例 0。”看起来只有在没有刻度的情况下指定精度时,刻度才为 0。如果两者都没有指定,它“创建一个列,其中可以存储任何精度和比例的数值,直到精度的实现限制。这种列不会将输入值强制为任何特定比例。”对我来说,这似乎是对开发人员非常友好的行为——这似乎是一个不错的选择。可能值得调整您的答案以反映这一点。【参考方案2】:
要获得尽可能好的(和精确的)精度,您可以使用NUMERIC
(或其别名DECIMAL
),它具有很高的精度,可以让您决定所需的精度;
数字
用户指定精度,精确到小数点前131072位;小数点后最多 16383 位
【讨论】:
很好的答案,除了 DECIMAL 不是 NUMERIC 的别名。虽然非常相似,但 SQL 规范允许数据库实现以比您使用 DECIMAL 列指定的精度更高的精度存储数据。对于 NUMERIC 列,规范要求数据库实现尊重您指定的精度。 进一步说明:SQL 规范要求两种数据类型都遵守您指定的比例。要亲自查看规范,请参阅 SQL:92 规范草案,第 6.1 节规则 17 和 18。 在 Postgres 实现中:The types decimal and numeric are equivalent. Both types are part of the SQL standard.
如所述 here
@Genma 正如我所说和展示的,这些类型在 SQL 标准和其他数据库中是不等效的。这是 Postgres 偏离标准的少数几个地方之一,而且不是很好。因此将这两种类型视为等价是不明智的。详情请见my Answer。【参考方案3】:
一般来说,钱不应该存储为浮点数。最好的方法通常是将金额存储为最小允许大小的整数(例如,一美分),并将其格式化以供输入和显示。这本质上就是在 SQL 中固定精度的DECIMAL
列所做的事情,但是如果将其传输回 Java,您仍然冒着丢失精度的风险(例如,如果您正好拆分最后一个允许数字的一半会发生什么情况)?
【讨论】:
以上是关于使用 Java 在 PostgreSQL 上的货币数据的主要内容,如果未能解决你的问题,请参考以下文章
将 Openshift 上的 PostgreSQL 连接到 Amazon S3
woocommerce上的多种货币,可根据位置自动切换[关闭]
Money-rails Gem 货币符号在某些设备上的重叠问题