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

时间:2009-02-24 17:54:39

标签: sql-server types

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

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

我应该继续使用money数据类型,还是使用decimal来获益? Money输入的字符较少,但这不是一个正当理由:)

12 个答案:

答案 0 :(得分:287)

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

运行此以查看我的意思:

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

答案 1 :(得分:259)

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

得出正确的结果:

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

money只要你不需要超过4位十进制数就好,而且你确保你的标量 - 不代表钱 - 是decimal s。

答案 2 :(得分:54)

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

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

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 / int money的文字表示中的小数点是视觉上的绒毛,只是就像yyyy-mm-dd日期的破折号一样。 SQL实际上并没有在内部存储它们。

关于decimal vs money,选择适合您需求的任何内容。存在money类型是因为将会计值存储为单位的1/10000的整数倍是非常常见的。此外,如果您正在处理除简单加法和减法之外的实际金钱和计算,您不应该在数据库级别执行此操作!在应用程序级别使用支持{{3的库}}

答案 3 :(得分:40)

我意识到WayneM已经声明他知道钱是特定于SQL Server的。然而,他问是否有任何理由使用小数点钱或反之亦然,我认为仍然应该说明一个明显的原因,即使用小数意味着如果你曾经有过这样的话,可以少担心一件事改变你的DBMS - 这可能发生。

让您的系统尽可能灵活!

答案 4 :(得分:31)

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

因此,当您正在处理的内容为MONEY时,使用MONEY并使用INT完全'安全'且适当,并根据其遵循的数学规则使用它(与MONEY eger相同)。

如果SQL Server将DECIMAL的分割和乘法推进到FLOAT s(或INT s?)会不会更好? - 可能,但他们没有选择做这个;他们也没有选择在划分FLOAT时将MONEY推广到DECIMAL

SUM(x)没有精确问题; x在计算过程中使用更大的中间类型只是使用该类型的一个“特征”(我实际上并不确定'特征'延伸到多远)。

回答具体问题,一个“令人信服的理由”?好吧,如果您希望在DECIMALMONEY可以是MONEYSMALLMONEY的绝对最高性能,则214,748.3647将有优势。

此外,不要忘记它是较小的堂兄,DECIMAL - 只有4个字节,但它在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}}舍入而不是{{1}}截断 - 与整数相同。)

答案 5 :(得分:12)

作为对抗其他答案的一般主旨的对立面。请参阅SQLCAT's Guide to Relational Engine

中的货币的许多好处......数据类型!

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

  

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

所以问题的答案是“它取决于”。您需要对某些算术运算更加小心以保持精度,但您可能会发现性能考虑使这一点值得。

答案 6 :(得分:10)

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

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

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

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

答案 7 :(得分:6)

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

MONEY Pro:

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

    MONEY的使用速度更快,因为它本来就是(作为金钱)。多快?我对100万个数据进行的简单SUM测试显示,MONEY为275 ms,NUMERIC为517 ms ...... 几乎快两倍 ...为什么SUM测试?请参阅下一页 Pro

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

MONEY Con:

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

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

答案 8 :(得分:2)

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

小数在内部以十进制表示,因此小数点的位置也基于基数10。这使得它的小部分完全代表它的价值,就像金钱一样。区别在于十进制的中间值可以保持精确到38位。

对于浮点数,该值以二进制形式存储,就像它是一个整数一样,十进制(或二进制,ahem)点的位置相对于表示该数字的位。因为它是二进制小数点,所以基数为10的数字会在小数点后立即失去精度。以这种方式不能精确地表示1/5或0.2。金钱和小数都没有受到这种限制。

很容易将资金转换为小数,执行计算,然后将结果值存回货币字段或变量。

从我的POV,我想要发生在数字上的事情,而不必过多考虑他们。如果所有计算都将转换为十进制,那么对我来说我只想使用十进制。为了显示目的,我会省钱。

尺寸方面我没有看到足够的改变我的想法。钱需要4-8个字节,而十进制可以是5,9,13和17.这9个字节可以覆盖8个字节的钱所能达到的整个范围。索引方式(比较和搜索应具有可比性)。

答案 9 :(得分:1)

我刚看到此博客文章: Money vs. Decimal in SQL Server

这基本上说钱有精确问题......

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个部分进行计算,但这个例子确实暴露了一些限制。

答案 10 :(得分:1)

我找到了在准确度主题中使用十进制数钱的原因。

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

只需测试并做出决定。

答案 11 :(得分:0)

以前的所有帖子都给出了有效的观点,但有些并不能准确回答问题。

问题是:当我们已经知道货币数据类型不太精确,如果在复杂的计算中使用它会导致错误,为什么有人会偏爱货币呢?

当您不进行复杂的计算时便会花钱,并且可以将这种精度用于其他需求。

例如,当您不必进行上述计算并且需要节省1个字节时(金钱需要8个字节,而十进制(19,4)需要9个字节)。

另一个例子,当您不必进行上述计算并且需要速度来处理大量数据时。

另一个示例,当您不必进行上述计算时,需要从有效的货币文本字符串中导入。尝试使用其他数字类型:SELECT CONVERT(MONEY,'$ 1,000.68')