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

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

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


当前回答

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

使您的系统尽可能灵活!

其他回答

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

使您的系统尽可能灵活!

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

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

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

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

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

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

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

我喜欢钱!它比DECIMAL少一个字节,而且计算执行得更快,因为(实际上)加法和减法操作本质上是整数操作。@SQLMenace的例子——这对不了解情况的人是一个很大的警告——同样可以应用于整数,其结果将为零。但这并不是不使用整数的理由——在适当的地方。

所以,当你处理的是金钱,并根据它遵循的数学规则(与整数相同)使用金钱时,使用金钱是完全“安全”和适当的。

如果SQL Server将MONEY的除法和乘法转换为小数(或浮点数?)会更好吗?可能,但他们没有选择这样做;在除法时,他们也没有选择将整数提升为浮点数。

金钱没有精确度问题;小数在计算过程中使用更大的中间类型只是使用该类型的一个“特征”(我实际上不确定这个“特征”延伸了多远)。

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

另外,不要忘记它的小表兄弟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

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

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

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

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

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

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

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

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


货币类型是整数

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

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

关于十进制和金钱,选择任何适合你需要的。货币类型的存在是因为将会计值存储为单位的1/ 10000000的整数倍数是非常常见的。此外,如果您正在处理实际的货币和计算,而不仅仅是简单的加减法,那么您不应该在数据库级别上这样做!在应用程序级别使用支持Banker's舍入的库进行计算(IEEE 754)