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

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

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


当前回答

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

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

十进制结果 > 1.000000

MoneyResult > 0.9999

FloatResult > 1

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

其他回答

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

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

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

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

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

钱是好的,只要你不需要超过4个十进制数字,你要确保你的标量(不代表钱)是小数。

我喜欢钱!它比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截断——与整数相同。)

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

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

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

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

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

十进制结果 > 1.000000

MoneyResult > 0.9999

FloatResult > 1

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