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

我很好奇money数据类型和类似decimal(19,4)的数据类型之间是否有真正的区别(这是货币内部使用的,我相信)。

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

我是否应该继续使用货币数据类型,或者使用十进制是否有好处?金钱可以输入的字符更少,但这不是一个有效的理由:)

478920 次浏览

永远不要使用金钱。它不精确,纯粹是垃圾;总是使用十进制/数字。

运行这个来看看我的意思:

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

输出: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

SQLMenace说钱是不精确的。但是你不能把钱乘以/除以钱!3美元乘以50美分等于多少?150 dollarcents ?你用标量乘(除)钱,结果应该是小数。

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

结果在正确的结果:

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

money很好,只要你不需要超过4个十进制数字,并且你要确保你的标量——不代表钱——是__abc1。

我刚刚看到这个博客条目: SQL Server中的金钱vs. Decimal

这基本上是说金钱有一个精确的问题……

declare @m money
declare @d decimal(9,2)


set @m = 19.34
set @d = 19.34


select (@m/1000)*1000
select (@d/1000)*1000

对于money类型,你将得到19.30而不是19.34。我不确定是否存在将钱分成1000份进行计算的应用场景,但这个示例确实暴露了一些局限性。

我们刚刚遇到了一个非常相似的问题,我现在非常+1,因为除了在顶级演示中,我从不使用Money。我们有多个表(实际上是一个销售凭证和一个销售发票),由于历史原因,每个表都包含一个或多个Money字段,我们需要按比例计算出总发票中有多少Tax与销售凭证上的每一行相关。我们的计算是

vat proportion = total invoice vat x (voucher line value / total invoice value)

这将导致现实世界中的货币/货币计算,从而导致在分割部分上的刻度错误,然后乘以一个不正确的增值税比例。当这些价值随后相加时,我们最终得到的是增值税比例的总和,加起来不等于发票总价值。如果括号中的任何一个值是小数(我将把其中一个转换为小数),vat比例将是正确的。

当括号不存在的时候,原来这是工作的,我猜是因为涉及到更大的值,它有效地模拟了一个更高的规模。我们添加括号是因为它先做乘法运算,这在一些罕见的情况下会破坏计算的精度,但这现在已经导致了这个更常见的错误。

我意识到WayneM已经声明他知道钱是特定的SQL Server。然而,他问是否有任何理由使用货币而不是十进制,反之亦然,我认为一个明显的原因仍然应该说明,那就是使用十进制意味着如果你不得不改变你的DBMS,它可以减少一件担心的事情——这是可能发生的。

使您的系统尽可能灵活!

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

即使是高精度的十进制类型也无法扭转局面:

declare @num1 numeric(38,22)
declare @num2 numeric(38,22)
set @num1 = .0000006
set @num2 = 1.0
select @num1 * @num2 * 1000000

1.000000 <-应该是0.6000000


money类型是整数

smallmoneydecimal(10,4)的文本表示可能看起来很像,但这并不意味着它们可以互换。当你看到日期存储为varchar(10)时,你会感到畏缩吗?这是一样的。

在幕后,money/smallmoney只是一个bigint/intmoney的文本表示中的小数点是视觉上的错误,就像yyyy-mm-dd日期中的破折号一样。SQL实际上并没有在内部存储这些数据。

关于decimalmoney,选择适合您需要的任何一个。money类型的存在是因为将会计值存储为1/10000th单位的整数倍数是非常常见的。同样,如果你处理的是实际的金钱和计算,而不是简单的加减法,在应用程序级使用支持银行家舍入(IEEE 754)的库来完成

嗯,我喜欢MONEY!它比DECIMAL少一个字节,而且计算执行得更快,因为(在幕后)加法和减法操作本质上是整数操作。@SQLMenace的example—对于不知道的人是一个很大的警告,同样可以应用于INTegers,其结果将为零。但这不是不使用integers—在适当的地方的理由。

因此,当你处理的是MONEY并根据它遵循的数学规则(与INTeger相同)使用MONEY是完全“安全”和适当的。

如果SQL Server将MONEY的除法和乘法转换为__abc1(或__abc2 ?)可能会更好,但他们没有选择这样做;在划分他们的时候,他们也没有选择将INTegers提升为FLOATs。

MONEY没有精度问题;__abc1在计算过程中使用更大的中间类型只是使用该类型的一个“特征”(我实际上不确定该“特征”扩展了多远)。

要回答具体的问题,一个“令人信服的理由”?好吧,如果你想在SUM(x)中获得绝对的最大性能,其中x可以是DECIMALMONEY,那么MONEY将有一条边。

另外,不要忘记它的小表弟SMALLMONEY—只有4个字节,但它确实在214,748.3647处达到最大值——这对于money—来说是非常小的,所以通常不是很适合。

为了证明使用更大的中间类型的重要性,如果你显式地将中间类型赋值给一个变量,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

产生2950.0000(好吧,所以至少是DECIMAL四舍五入,而不是MONEY截断—与整数相同。)

我找到了在精确度科目中使用十进制而不是货币的原因。

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

DecimalResult > 1.000000

MoneyResult > 0.9999

FloatResult > 1 .执行以下命令

只需测试一下,然后做出决定。

当你需要对值进行乘法/除法运算时,你不应该使用金钱。货币的存储方式与整数的存储方式相同,而十进制则以小数点和十进制数字的形式存储。这意味着货币在大多数情况下会降低精度,而十进制只有在转换回原始刻度时才会如此。货币是固定点,所以它的规模在计算过程中不会改变。但是,由于它作为十进制字符串打印时是一个固定点(而不是以2为基数的字符串中的一个固定点),因此精确地表示了4以内的值。所以对于加减法,钱是没问题的。

小数点内部以10为底表示,因此小数点的位置也以10为底数为基础。这使得它的小数部分准确地代表了它的值,就像货币一样。不同之处在于十进制的中间值可以保持38位的精度。

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

将货币转换为十进制,执行计算,然后将结果值存储回货币字段或变量中,这非常简单。

从我的角度来看,我希望发生在数字上的事情不需要太多思考就能发生。如果所有的计算都要转换成十进制,那么对我来说,我只想用十进制。我会保留money字段用于显示。

就尺寸而言,我看不出有多大差别来改变我的想法。Money需要4 - 8个字节,而decimal可以是5、9、13和17。9个字节可以覆盖8个字节的货币所能覆盖的全部范围。索引方面(比较和搜索应该具有可比性)。

作为对其他答案主旨的反驳。参见SQLCAT关系引擎指南中的金钱的诸多好处:数据类型!

我要具体指出以下几点

在客户实现的过程中,我们发现了一些有趣的事情 有关货币数据类型的性能数字。例如,当 Analysis Services被设置为货币数据类型(从double)为 匹配SQL Server的金钱数据类型,有13%的改善 处理速度(行/秒)。在SQL中获得更快的性能 服务器集成服务(SSIS)在30分钟内加载1.18 TB 分钟,正如SSIS 2008 -世界纪录ETL表现所指出的那样,它是 观察到将四个十进制(9,2)列的大小更改为5 将TPC-H LINEITEM表中的字节改为money(8字节)改进的批量 插入速度20%…性能提升的原因是由于SQL Server的表格数据流(TDS)协议 关键设计原则是否以紧凑的二进制形式传输数据,并尽可能接近内部存储 SQL Server格式。在SSIS 2008 -世界纪录ETL性能测试中观察到这一点 Kernrate;当数据类型从十进制转换为货币时,协议显著下降。这使得 尽可能高效地传输数据。复杂数据类型需要额外的解析和CPU周期来处理 固定宽度类型。< / p >

所以这个问题的答案是“视情况而定”。对于某些算术操作,您需要更加小心,以保持精度,但您可能会发现,出于性能考虑,这样做是值得的。

基于我个人的专业知识和经验,我想对金钱和数值给出不同的看法。我的观点是金钱,因为我已经用了很长一段时间,从来没有真正使用数值…

资金支持:

  • < p > 原生数据类型。它使用与CPU寄存器(32或64位)相同的本机数据类型(整数),因此计算不需要不必要的开销,因此它是…MONEY需要8个字节,NUMERICAL(19,4)需要9个字节(大12.5%)…

    钱只要是用来(作为钱)的,就能跑得更快。有多快?我对100万数据的简单SUM测试显示MONEY是275毫秒,NUMERIC是517毫秒…这是几乎是原来的两倍…为什么是SUM测试?参见下一个 point

  • 最省钱。MONEY最好是用来储存钱和做操作的,例如,在会计中。在SUM操作完成后,单个报告可以运行数百万个加法(SUM)和一些乘法。对于非常大的会计应用程序,它是几乎是原来的两倍,它是非常重要的…
  • 货币精度低。现实生活中的金钱不需要非常精确。我的意思是,许多人可能关心1美分美元,但0.01美分美元呢?事实上,在我的国家,银行不再关心美分(十进制逗号后面的数字);我不知道美国银行或其他国家…

钱反对:

  • 有限的精度。MONEY只有四位数(在逗号之后)精度,所以在进行除法等运算之前必须对其进行转换…但话说回来,money不需要这么精确,它意味着被用作货币,而不仅仅是一个数字…

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

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

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

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

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

SELECT CONVERT(MONEY, '$1,000.68')

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

另一个例子,当你不需要进行这些计算(你只需要存储一个值),需要节省1个字节(金钱需要8个字节,而十进制(19,4)需要9个字节)。在一些应用中(快速CPU,大内存,慢IO),比如只是读取大量的数据,这也可以更快。