在十进制列中存储货币 - 精度和规模是多少?

时间:2008-10-22 04:08:19

标签: sql database database-design currency

我正在使用十进制列来存储数据库中的货币值,而今天我想知道要使用的精度和规模。

因为假设固定宽度的char列效率更高,我认为对于十进制列也是如此。是吗?

我应该使用什么精度和规模?我正在考虑精确24/8。这有点矫枉过正,还不够还是不错?


这就是我决定做的事情:

  • 将转换率(适用时)存储在事务表本身中,作为float
  • 将货币存储在帐户表
  • 交易金额为DECIMAL(19,4)
  • 使用转换率的所有计算都将由我的应用程序处理,因此我可以控制舍入问题

我不认为转换率的浮点数是一个问题,因为它主要用于参考,而且无论如何我都会将其转换为小数。

感谢大家的宝贵意见。

10 个答案:

答案 0 :(得分:158)

如果你正在寻找一个通用的,我建议DECIMAL(19, 4)是一个受欢迎的选择(谷歌快速说明这一点)。我认为这源于旧的VBA / Access / Jet Currency数据类型,是该语言中的第一个定点十进制类型; Decimal仅在VB6 / VBA6 / Jet 4.0中以'版本1.0'样式(即未完全实现)。

固定点十进制值的存储的经验法则是存储至少比实际需要的小数位数多一个以允许舍入。将前端中的旧Currency类型映射到后端的DECIMAL(19, 4)类型的原因之一是Currency表现出银行家的性质舍入,而DECIMAL(p, s)舍入通过截断。

DECIMAL存储中的额外小数位数允许实现自定义舍入算法,而不是采用供应商的默认值(对于设计师期望所有值以结束的方式而言,银行家的四舍五入是令人震惊的,至少可以说.5从零开始圆。)

是的,DECIMAL(24, 8)听起来有点矫枉过正。大多数货币都是小数点后四位或五位。我知道需要小数为8(或更多) 的情况,但这是一个'正常'货币金额(比如说四个小数位)按比例分配的地方,这意味着小数精度应该相应减少(在这种情况下也考虑浮点类型)。现在没有人有这么多钱要求小数精度为24:)

然而,一些研究可能是有序的,而不是一刀切的方法。向您的设计师或领域专家咨询可能适用的会计规则:GAAP,EU等。我模糊地回顾一些具有明确规则的EU内部状态转移,用于舍入到小数点后五位,因此使用DECIMAL(p, 6)进行存储。会计师通常似乎支持四位小数。


PS避免使用SQL Server的MONEY数据类型,因为它在舍入时存在严重的准确性问题,以及其他注意事项,例如可移植性等。请参阅Aaron Bertrand's blog


微软和语言设计师选择了银行家的四舍五入,因为硬件设计师选择了它[引用?]。例如,它被载入电气和电子工程师协会(IEEE)标准。硬件设计师选择它是因为数学家更喜欢它。见Wikipedia;换句话说:1906年的“概率论和错误论”称这是“计算机的规则”(“计算机”意味着执行计算的人。)

答案 1 :(得分:92)

我们最近实施了一个系统,需要处理多种货币的价值并在它们之间进行转换,并且很难找到一些方法。

永远不要使用浮动点数来赚钱

浮点算术引入了一些不准确的问题,直到他们搞砸了一些东西才会被注意到。所有值都应该存储为整数或固定小数类型,如果您选择使用固定小数类型,那么请确保您确切了解该类型的内容(即内部使用整数或浮点数)类型)。

当您需要进行计算或转换时:

  1. 将值转换为浮点
  2. 计算新值
  3. 将数字四舍五入并将其转换回整数
  4. 在步骤3中将浮点数转换回整数时,不要只是强制转换它 - 使用数学函数先将其舍入。这通常是round,但在特殊情况下可能是floorceil。了解差异并谨慎选择。

    将数字类型与值

    一起存储

    如果您只处理一种货币,这对您来说可能并不重要,但对我们来说处理多种货币非常重要。我们使用3字符代码表示货币,例如美元,英镑,日元,欧元等。

    根据具体情况,存储可能也会有所帮助:

    • 该号码是税前还是税后(税率是多少)
    • 该号码是转换的结果(以及转换后的内容)

    了解您正在处理的数字的准确性范围

    对于实际值,您希望与货币的最小单位一样精确。这意味着您没有小于1美分,1美分,日元,分钟等的值。不要存储比无理由更高精度的值。

    在内部,您可以选择处理较小的值,在这种情况下,这是不同类型的货币值。确保您的代码知道哪些是哪个,哪些不会混淆。即使在这里也避免使用浮点值。


    将所有这些规则加在一起,我们决定遵守以下规则。在运行代码时,货币使用整数存储为最小单位。

    class Currency {
       String code;       //  eg "USD"
       int value;         //  eg 2500
       boolean converted;
    }
    
    class Price {
       Currency grossValue;
       Currency netValue;
       Tax taxRate;
    }
    

    在数据库中,值以下列格式存储为字符串:

    USD:2500
    

    存储价值25.00美元。我们能够做到这一点只是因为处理货币的代码不需要在数据库层本身内,因此所有值都可以先转换为内存。其他情况无疑会适用于其他解决方案。


    如果我之前没有说清楚,不要使用浮动!

答案 2 :(得分:3)

在MySQL中处理资金时,如果您知道资金价值的精确度,请使用DECIMAL(13,2);如果您只想要快速足够好的近似值,请使用DOUBLE。 因此,如果您的应用程序需要处理高达一万亿美元(或欧元或英镑)的货币价值,那么这应该有效:

DECIMAL(13, 2)

或者,如果您需要遵守GAAP,请使用:

DECIMAL(13, 4)

答案 3 :(得分:2)

小数点后4位将为您提供存储世界上最小货币子单位的准确性。如果您需要微支付(纳米支付?!)准确度,您可以进一步降低它。

我也更喜欢DECIMAL到特定于DBMS的资金类型,在应用程序IMO中保持这种逻辑更安全。沿着同一行的另一种方法就是使用[long]整数,将格式化为¤unit.subunit,以便在应用程序级别完成人类可读性(¤=货币符号)。

答案 4 :(得分:1)

SQL Server上的money数据类型在小数点后有四位数。

从SQL Server 2000联机丛书:

货币数据代表正数或负数。在Microsoft®SQLServer™2000中,使用money和s​​mallmoney数据类型存储货币数据。货币数据可以存储到小数点后四位的精度。使用货币数据类型存储的值范围为-922,337,203,685,477.5808至+922,337,203,685,477.5807(需要8个字节来存储值)。使用smallmoney数据类型存储-214,748.3648到214,748.3647范围内的值(需要4个字节来存储值)。如果需要更多的小数位,请改用小数数据类型。

答案 5 :(得分:1)

有时你需要花费不到一美分,并且有些国际货币使用非常大的恶魔。例如,您可能会向每位客户收取0.088美分的费用。在我的Oracle数据库中,列被定义为NUMBER(20,4)

答案 6 :(得分:1)

如果您要在数据库中进行任何类型的算术运算(乘以计费率等),您可能需要比此处建议的人更精确,原因与您相同永远不要在应用程序代码中使用任何小于双精度浮点值的东西。

答案 7 :(得分:0)

如果您使用的是IBM Informix Dynamic Server,那么您将拥有MONEY类型,这是DECIMAL或NUMERIC类型的次要变体。它始终是定点类型(而DECIMAL可以是浮点类型)。您可以指定1到32之间的刻度,以及0到32之间的精度(默认为16,精度为2)。因此,根据您需要存储的内容,您可能会使用DECIMAL(16,2) - 仍然足以将美国联邦赤字持有到最接近的分数 - 或者您可以使用较小的范围或更多小数位。

答案 8 :(得分:0)

我认为,在很大程度上,您或您客户的要求应决定使用的精度和规模。例如,对于我正在处理的仅涉及英镑货币的电子商务网站,我被要求将其保持为十进制(6,2)。

答案 9 :(得分:0)

这里的答案很晚,但我已经使用了

DECIMAL(13,2)

我认为应该允许最多99,999,999,999.99。