您应该在SQL Server中选择MONEY或DECIMAL(x,y)数据类型吗?

Way*_*ina 420 sql-server types

我很好奇money数据类型和类似之间是否存在真正的差异decimal(19,4)(我认为这是内部使用的钱).

我知道这money是特定于SQL Server的.我想知道是否有令人信服的理由选择一个而不是另一个; 大多数SQL Server示例(例如AdventureWorks数据库)使用money而不是decimal价格信息之类的东西.

我应该继续使用money数据类型,还是使用十进制代替?Money输入的字符较少,但这不是有效的原因:)

SQL*_*ace 308

你永远不应该用钱.它不精确,是纯垃圾; 总是使用十进制/数字.

运行这个看看我的意思:

DECLARE
    @mon1 MONEY,
    @mon2 MONEY,
    @mon3 MONEY,
    @mon4 MONEY,
    @num1 DECIMAL(19,4),
    @num2 DECIMAL(19,4),
    @num3 DECIMAL(19,4),
    @num4 DECIMAL(19,4)

    SELECT
    @mon1 = 100, @mon2 = 339, @mon3 = 10000,
    @num1 = 100, @num2 = 339, @num3 = 10000

    SET @mon4 = @mon1/@mon2*@mon3
    SET @num4 = @num1/@num2*@num3

    SELECT @mon4 AS moneyresult,
    @num4 AS numericresult
Run Code Online (Sandbox Code Playgroud)

产量:2949.0000 2949.8525

对于那些说你不用钱分钱的人:

这是我计算相关性的一个查询,将其更改为货币会产生错误的结果.

select t1.index_id,t2.index_id,(avg(t1.monret*t2.monret)
    -(avg(t1.monret) * avg(t2.monret)))
            /((sqrt(avg(square(t1.monret)) - square(avg(t1.monret))))
            *(sqrt(avg(square(t2.monret)) - square(avg(t2.monret))))),
current_timestamp,@MaxDate
            from Table1 t1  join Table1 t2  on t1.Date = traDate
            group by t1.index_id,t2.index_id
Run Code Online (Sandbox Code Playgroud)

  • "从不"是一个强有力的词."Money"对于将结果投射到该类型以便以文化敏感的方式显示给用户非常有用,但是你认为用于计算本身非常糟糕. (57认同)
  • 除了"钱"之外,"钱"的乘法和除法,这个"插图"是操纵性的.有记录的事实是,"钱"具有固定且非常有限的精度.在这种情况下,首先应该乘以,然后除以.在此示例中更改运算符的顺序,您将得到相同的结果.一个`money`本质上是一个64位的int,如果你要处理整数,你会在分割之前成倍增加. (50认同)
  • 你的例子没有意义,因为没有人会将两个类型的金钱对象相乘.如果你想证明自己的观点,你需要比较一个小数乘以小数乘以​​小数乘以​​小数. (31认同)
  • ..但仍然令人费解的是为什么钱*钱没有钱的精确度. (25认同)
  • @Learning:确实有钱.但是,您仍然会遇到可能随时间累积的舍入错误.十进制类型不使用二进制算法:它保证它在纸上完成相同的基础10结果. (4认同)
  • 我认为双方都有好的观点.我从中获取的将是继续使用小数,但我偶尔会使用Money来显示. (3认同)
  • 这个例子实际上表明“decimal”被破坏了。这里的最高阶运算符是除数,这意味着您将 100 除以 339,然后结果受到货币类型中 4 位小数的限制。将此值乘以 10000 永远不会得到小数值。在我看来,decimal(19,4) 存储的精度高于 4 的事实是一个错误。 (2认同)
  • 再看一遍,他们都是错的。如果您将步长降低,则Decimal在2950处正确。这是因为它正确地将中间步长从0.8525向上舍入,而将金钱向下舍入。因此正确答案应该是2950,而不是2949或2949.8525。 (2认同)
  • -1因为"纯垃圾"是一种主观判断.但主要是因为@configurator提供的答案. (2认同)
  • 我认为他们将使用PC上设置的区域值,所以它可以在美国PC上获得50美元,在没有任何特殊转换过程的情况下在法国PC上获得50欧元.所以我决定使用十进制作为准确和可维护的类型.请阅读我的帖子关于十进制类型的准确性的其他原因. (2认同)

con*_*tor 264

SQLMenace表示资金不准确.但是你不要用钱来分钱/分钱!3美元乘50美分多少钱?150美元?你用标量乘以/除以钱,这应该是十进制的.

DECLARE
@mon1 MONEY,
@mon4 MONEY,
@num1 DECIMAL(19,4),
@num2 DECIMAL(19,4),
@num3 DECIMAL(19,4),
@num4 DECIMAL(19,4)

SELECT
@mon1 = 100,
@num1 = 100, @num2 = 339, @num3 = 10000

SET @mon4 = @mon1/@num2*@num3
SET @num4 = @num1/@num2*@num3

SELECT @mon4 AS moneyresult,
@num4 AS numericresult
Run Code Online (Sandbox Code Playgroud)

得到正确的结果:

moneyresult           numericresult
--------------------- ---------------------------------------
2949.8525             2949.8525

money只要您不需要超过4位十进制数字就可以了,并且确保您的标量 - 不代表金钱 - 是decimal.

  • 一美元钞票可以获得多少1美分的硬币?对此的回答需要金钱/金钱. (51认同)
  • @Learning在这种情况下,结果不是钱,而是浮动.(基本尺寸分析.) (47认同)
  • @mson他是正确的,不要像你用一行评论那样做个人批评,这没有用.如果他没有除以0.01美元而是0.01,那么结果是100美元而不是100美元.一美元100美分,而不是100美分.单位很重要!绝对是潜水的好地方,也许是金钱的倍增. (24认同)
  • 如果我想花1000美元购买价格为36美元的股票,这不是"钱/钱"的合法用法吗?答案是整个单位没有附上美元符号,这是正确的!这是一个非常合理的场景,暗示由于像这样的奇怪边缘情况,`money`不是一个好的数据类型,因为结果总是被截断回来适合`money`而不是遵循正常的精度规则和规模.如果你想计算一组'money`值的标准偏差怎么办?是的,`Sqrt(Sum(money*money))`(简化)实际上确实有意义. (18认同)
  • @Learning:你问数据库一美元多少美分?无论如何,这将返回正确的结果.他的问题是钱/钱只精确到四位数(它是0.2949),然后当乘以10000它变成2949.0000. (11认同)
  • @learning你的评论表明你不懂科学和转换.如果你确实需要计算一美元钞票给你多少1美分的硬币,你不会除以1美分,而是除以精确的转换.在这种情况下的转换是准确的,并不以任何方式支持您的意图. (3认同)
  • 那么我可以用什么比特币? (3认同)
  • 当然可以用钱来分钱!!!每个利率计算都是用钱除以货币。如果我投资 200 美元赚取 12.00 美元,则利率为 12 美元 ÷ 200 美元 = 0.06 = 6%。更重要的是,维度分析(会计师应该强制执行)表明,由于 $ ÷ $ = 1,单位没有维度(即 0.06 或 6% 只是一个数字 - 没有度量单位)。但是用金钱乘以金钱的单位是 $²,这没有我想象的意义。你可以用平方美元衡量什么? (3认同)
  • 汇率是货币除以货币的另一个有效例子。事实上,XR计算中允许有多少位数字,以及每种货币允许有多少位数字,都是有规则的。当涉及 XR 时,这使得具有硬编码精度的“money”类型不适合保存货币值 (2认同)
  • @PanagiotisKanavos人们很容易认为你是用汇率划分货币,但它仍然是货币的数量.汇率本身仍然是标量,而不是金钱.我同意这种钱类型足够混乱,以避免在可能的情况下使用. (2认同)
  • @SteveSether 不,汇率不是标量。它需要两种货币才有意义。另一方面,“货币”类型是 - 它没有单位,只有一个*假设*它可以用于存储货币数据。无论如何,这不是重点。您不能假设任意精度 4 就足够了。不同的货币具有不同的精度(例如,日元没有小数位,**比特币有 8**),并且有*特定规则*来控制在转换和计算过程中应使用多少位数字 (2认同)
  • 这基本上只是说“可以得到错误的答案,因为在技术上语义上奇数的计算中它只会得到错误的答案”。好的,但是还是错了。为什么不使用即使在语义上奇怪的情况下也不会出错的类型?(换句话说,“优势”是什么?) (2认同)
  • @ReversedEngineer - 我喜欢关于将钱除以钱的解释。当您说“利率为 12 美元/200 美元”时,我们可以将除法符号读作“每”。每投入 200 美元我们就赚了 12 美元 = 有效的数学。 (2认同)

Ano*_*non 57

如果你不知道自己在做什么,一切都很危险

即使是高精度的十进制类型也无法节省一天:

declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000
Run Code Online (Sandbox Code Playgroud)

1.000000 < - 应该是0.6000000


money类型是整数

文本表示smallmoneydecimal(10,4)可能看起来相似,但这并不能使它们互换.当你看到日期存储为时,你会畏缩varchar(10)吗?这是一回事.

在幕后,money/ smallmoney只是一个bigint/ int 文本表示中的小数点money是视觉绒毛,就像yyyy-mm-dd日期中的破折号一样.SQL实际上并不在内部存储它们.

关于decimalvs money,选择适合您需求的任何内容.的money存在类型,因为储存计费值作为单位的1 /第一万整数倍是很常见的.此外,如果您正在处理除简单加法和减法之外的实际金钱和计算,您不应该在数据库级别执行此操作! 使用支持Banker Rounding(IEEE 754)的库在应用程序级别执行此操作

  • 规模溢出.在小尺度上,数字(a,b)*数字(c,d)产生数字(a-b + c-d + 1,max(b,d)).但是,如果(a + b + c + d)> 38,则SQL会限制比例,从分数侧抢夺精度以填充整数侧,从而导致舍入误差. (4认同)
  • 我认为这是最具启发性的答案,因为你不仅仅解释了传播的错误,而且解释了场景背后的真实情况,以及为什么MONEY首先存在.我不确定为什么花了4年时间才能得到这个答案,但也许是因为太多关注计算"问题",而不是关注金钱与小数的原因和方法. (3认同)

Dea*_*ean 40

我意识到WayneM已经声明他知道钱是特定于SQL Server的.但是,他问是否有任何理由使用小数点数或反之亦然,我认为仍然应该说明一个明显的原因并且使用小数意味着如果你不得不改变你的DBMS,那就更少担心了 - 这可能发生.

使您的系统尽可能灵活!

  • 有可能,但理论上您可以在另一个 DBMS(支持域声明)中声明一个名为 Money 的域。 (2认同)
  • 作为目前正在将 SQL Server 数据库转换为 Amazon Redshift 的人,我可以保证这是一个真正的问题。尽量避免为特定数据库平台定制的数据类型,除非有非常充分的业务理由来使用它们。 (2认同)

dsz*_*dsz 30

好吧,我喜欢MONEY!它比一个字节便宜DECIMAL,并且计算执行得更快,因为(在封面下)加法和减法操作基本上是整数运算.@ SQLMenace的例子 - 对于不知道的人来说是一个很好的警告 - 同样可以应用于INTegers,结果为零.但是,没有理由不在适当的地方使用整数.

因此,MONEY当您正在处理的内容是MONEY根据其遵循的数学规则(与INTeger 相同)使用时,它非常"安全"且适合使用.

如果SQL Server提升MONEYs 和DECIMALs(或FLOATs?)的分割和乘法会更好- 可能,但他们没有选择这样做; 他们也没有选择在分裂时促进INT艾格斯FLOAT.

MONEY没有精确问题; 那DECIMAL得面对具有计算过程中使用较大的中间类型仅仅是使用该类型的"功能"(我不肯定实际上多远,即"功能"延伸).

回答具体问题,一个"令人信服的理由"?好吧,如果你想在一个或两个SUM(x)地方绝对最大的表现,那么就会有优势.xDECIMALMONEYMONEY

另外,不要忘记它是一个较小的堂兄,SMALLMONEY只需要4个字节,但它最大限度地214,748.3647用于 - 这对于钱而言非常小 - 所以通常不太合适.

为了证明使用更大的中间类型的观点,如果将中间体明确地分配给变量,则会DECIMAL遇到同样的问题:

declare @a decimal(19,4)
declare @b decimal(19,4)
declare @c decimal(19,4)
declare @d decimal(19,4)

select @a = 100, @b = 339, @c = 10000

set @d = @a/@b

set @d = @d*@c

select @d
Run Code Online (Sandbox Code Playgroud)

产生2950.0000(好吧,所以至少是DECIMAL圆形而不是MONEY截断 - 与整数相同.)

  • `MONEY`比一个*大*`DECIMAL`少一个字节,精度高达19位.但是,大多数现实世界的货币计算(高达9.99美元)都可以装入`DECIMAL(9,2)`,这只需要5个字节.您可以节省大小,减少对舍入错误的担忧,*和*使您的代码更具可移植性. (19认同)
  • @JonofAllTrades是一个很好的观点. (2认同)
  • 尝试存储比特币值。它有8位小数 (2认同)

小智 12

我们刚刚遇到了一个非常类似的问题,我现在非常喜欢+1,因为除了顶层演示之外从不使用Money.我们有多个表(实际上是销售凭证和销售发票),其中每个表都包含一个或多个货币字段,这是出于历史原因,我们需要按比例计算以计算总发票税与每个税额的相关程度.销售凭证上的行.我们的计算是

vat proportion = total invoice vat x (voucher line value / total invoice value)
Run Code Online (Sandbox Code Playgroud)

这导致现实世界的货币/货币计算导致分割部分的尺度误差,然后乘以不正确的增量比例.当随后添加这些值时,我们最终得到的桶数比例总和不会与总发票金额相加.如果括号中的任何一个值都是小数(我即将投出其中一个),那么增值比例就是正确的.

当括号不存在时,原来这个用来工作,我想因为涉及的值越大,它就能有效地模拟更高的尺度.我们添加了括号,因为它首先进行乘法运算,这在极少数情况下会超出可用于计算的精度,但现在这已经导致了这个更常见的错误.

  • 但它只是这样做,因为一个无知的开发人员忽略了增值税计算的规则和记录的货币精确限制. (8认同)
  • @TomTom 但你关于滥用这种数据类型的可能性的观点是一个支持完全避免使用它的论点。 (2认同)
  • @TomTom 可悲的是,有许多不称职的开发人员(以及许多出色的开发人员)对我来说,除非我可以保证将来如何查询这些数据并且没有人会犯这里描述的那种错误,最好谨慎一点,使用十进制类型。也就是说,在阅读了所有这些答案后,我可以看到有一些特定的用例,其中货币将是最佳使用类型,除非有一个非常好的用例,否则我不会使用它(例如,列只会永远SUM聚合,批量加载大量财务数据)。 (2认同)

Mar*_*ith 12

作为对抗其他答案的一般主旨的对立点.查看货币的许多好处......数据类型!SQLCAT的关系引擎指南中

具体来说,我会指出以下内容

在客户实施方面,我们发现了一些有关货币数据类型的有趣性能数据.例如,当Analysis Services设置为货币数据类型(来自double)以匹配SQL Server货币数据类型时,处理速度(行/秒)提高了13%.为了在SQL Server Integration Services(SSIS)中获得更快的性能,在30分钟内加载1.18 TB,如SSIS 2008中所述 - 世界记录ETL性能,观察到更改四个小数(9,2)列的大小为在TPC-H LINEITEM表中5个字节到钱(8个字节),批量插入速度提高了20%......性能提升的原因是因为SQL Server的表格数据流(TDS)协议,它具有以紧凑二进制形式传输数据并尽可能接近SQL Server内部存储格式的关键设计原则.根据经验,这在SSIS 2008期间观察到 - 使用Kernrate的世界纪录ETL性能测试; 当数据类型从十进制转换为货币时,协议显着下降.这使得数据传输尽可能高效.复杂数据类型需要额外的解析和CPU周期来处理,而不是固定宽度类型.

所以问题的答案是"它取决于".您需要对某些算术运算更加小心以保持精度,但您可能会发现性能方面的考虑使其值得.


Sus*_*ilo 6

我想对MONEY与NUMERICAL给出不同的看法,主要基于我自己的专业知识和经验...我的观点是MONEY,因为我已经使用了很长时间并且从未真正使用过NUMERICAL ... .

MONEY Pro:

  • 原生数据类型.它使用一个本地数据类型(整数)作为相同的CPU寄存器(32位或64位),所以计算不需要不必要的开销所以它的更小更快 ... MONEY需要8个字节和数值(19,4 )需要9个字节(大12.5%)......

    只要它用于它(因为钱),MONEY就会更快.多快?我SUM对100万个数据的简单测试表明,MONEY是275毫秒,NUMERIC是517毫秒...这几乎快两倍 ...为什么SUM测试?见下一个Pro

  • 最物有所值.MONEY最适合存储资金并进行操作,例如会计.在SUM操作完成后,单个报告可以运行数百万个添加(SUM)和一些乘法.对于非常大的会计应用,它几乎快两倍,而且非常重要......
  • 低精度的钱.现实生活中的金钱不需要非常精确.我的意思是,很多人可能只关心1美分,但是0.01美元怎么样呢?事实上,在我的国家,银行不再关心美分(小数点后的数字); 我不知道美国银行或其他国家......

钱币:

  • 精度有限.MONEY只有四位数(在逗号之后)精度,所以在进行除法之类的操作之前必须进行转换......但是再次money不需要那么精确并且意味着要用作钱,而不仅仅是数...

但是......大,但这里甚至你的应用程序涉及真钱,但不要在很多SUM操作中使用它,比如会计.如果你使用大量的除法和乘法,那么你不应该使用MONEY ......

  • @HaakonLøtveit这个完整的Q&A线程是关于SQL Server数据类型的"钱",所以我认为他们不需要在答案中指明这一点.在某些情况下(例如加载数据),金钱可以比使用十进制更快,有关详细信息,请参阅Martin Smith的答案.我同意这个答案的大部分内容,因为某些用例可能使Money比十进制更有效,但是除非有一个非常令人信服的使用它的情况,我认为应该避免. (2认同)

Web*_* K. 6

以前的所有帖子都提出了有效的观点,但有些没有准确回答问题。

问题是:当我们已经知道它是一种不太精确的数据类型并且如果用于复杂计算会导致错误时,为什么有人更喜欢它?

当您不会进行复杂的计算并且可以用这种精度来换取其他需求时,您就可以使用金钱。

例如,当您不必进行这些计算,并且需要从有效货币文本字符串导入数据时。此自动转换仅适用于 MONEY 数据类型:

SELECT CONVERT(MONEY, '$1,000.68')
Run Code Online (Sandbox Code Playgroud)

我知道您可以制作自己的导入程序。但有时您不想重新创建具有全球特定区域设置格式的导入例程。

另一个例子,当您不必进行这些计算(您只需要存储一个值)并且需要保存 1 个字节时(钱需要 8 个字节,decimal(19,4) 需要 9 个字节)。在某些应用程序(快速 CPU、大 RAM、慢 IO)中,例如读取大量数据,这也可以更快。


QMa*_*ter 5

我在准确性主题中找到了使用小数而不是金钱的原因。

DECLARE @dOne   DECIMAL(19,4),
        @dThree DECIMAL(19,4),
        @mOne   MONEY,
        @mThree MONEY,
        @fOne   FLOAT,
        @fThree FLOAT

 SELECT @dOne   = 1,
        @dThree = 3,    
        @mOne   = 1,
        @mThree = 3,    
        @fOne   = 1,
        @fThree = 3

 SELECT (@dOne/@dThree)*@dThree AS DecimalResult,
        (@mOne/@mThree)*@mThree AS MoneyResult,
        (@fOne/@fThree)*@fThree AS FloatResult
Run Code Online (Sandbox Code Playgroud)

小数结果 > 1.000000

金钱结果 > 0.9999

浮点结果 > 1

只需测试一下并做出决定。

  • 我们非常希望听到(即阅读)*您的*结论。 (5认同)

Ger*_*ill 5

当您需要对值进行乘法/除法时,您不应该使用金钱。货币的存储方式与整数的存储方式相同,而小数则存储为小数点和小数位。这意味着货币在大多数情况下都会降低准确性,而十进制只有在转换回原始比例时才会出现这种情况。货币是定点的,因此在计算过程中其规模不会改变。然而,由于它在打印为十进制字符串时是固定点(而不是基数为 2 的字符串中的固定位置),因此可以精确表示最大为 4 的值。所以加减法的话,钱就可以了。

小数内部以 10 为基数表示,因此小数点的位置也是基于 10 基数的。这使得它的小数部分准确地代表了它的价值,就像金钱一样。不同之处在于小数的中间值可以保持高达 38 位的精度。

对于浮点数,该值以二进制形式存储,就好像它是整数一样,并且小数点(或二进制,咳咳)点的位置相对于表示该数字的位。由于它是二进制小数点,因此以 10 为基数的数字会在小数点后失去精度。1/5 或 0.2 无法以这种方式精确表示。货币和小数都不受此限制。

将货币转换为十进制,执行计算,然后将结果值存储回货币字段或变量是很容易的。

从我的角度来看,我希望发生在数字上的事情能够自然发生,而不必过多考虑。如果所有计算都将转换为十进制,那么对我来说我只想使用十进制。我会保留钱字段以供显示之用。

从尺寸上看,我没有看到足够的差异来改变我的想法。Money 需要 4 - 8 个字节,而十进制可以是 5、9、13 和 17。9 个字节可以覆盖 8 个字节的 Money 可以覆盖的整个范围。索引方面(比较和搜索应该具有可比性)。