我很好奇 money
数据类型和 decimal(19,4)
之类的数据类型之间是否存在真正的区别(我相信这是钱在内部使用的东西)。
我知道 money
特定于 SQL Server。我想知道是否有令人信服的理由选择其中一个;大多数 SQL Server 示例(例如 AdventureWorks 数据库)使用 money
而不是 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
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
只要您不需要超过 4 个十进制数字,money
就很好,并且您确保您的标量(不代表金钱)是 decimal
。
money / money
的合法用法吗?答案是没有附加美元符号的整个单位,这是正确的!这是一个完全合理的场景,表明由于像这样奇怪的边缘情况,money
不是一个好的数据类型,因为结果总是被截断回适合 money
而不是遵循正常的精度规则和规模。如果要计算一组 money
值的标准差怎么办?是的,Sqrt(Sum(money * money))
(简化)确实有意义。
如果你不知道自己在做什么,一切都是危险的
即使是高精度的十进制类型也无法挽救这一天:
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
类型是整数
smallmoney
和 decimal(10,4)
的文本表示可能看起来很相似,但这并不意味着它们可以互换。当您看到存储为 varchar(10)
的日期时,您会畏缩吗?这是同一件事。
在幕后,money
/smallmoney
只是一个 bigint
/int
money
的文本表示中的小数点是视觉上的绒毛,就像 yyyy-mm-dd 日期中的破折号一样。 SQL 实际上并不在内部存储这些内容。
关于 decimal
与 money
,请选择适合您需要的任何内容。存在 money
类型是因为将记帐值存储为单位的 1/10000 的整数倍是很常见的。此外,如果您要处理的不是简单的加减法,而是实际的金钱和计算,您不应该在数据库级别这样做!使用支持 Banker's Rounding (IEEE 754) 的库在应用程序级别进行
我意识到 WayneM 曾表示他知道金钱是特定于 SQL Server 的。然而,他问是否有任何理由使用十进制的钱,反之亦然,我认为仍然应该说明一个明显的原因,那就是使用十进制意味着如果你不得不改变你的 DBMS,那就少了一件担心的事情- 这可能发生。
使您的系统尽可能灵活!
date
类型,我会说你会总是有这样的问题。你应该说“当数据库已经提供了更好、更符合标准的类型并且警告不推荐使用的类型时,不要使用不推荐使用的类型”。
嗯,我喜欢MONEY
!它比 DECIMAL
便宜一个字节,并且计算执行得更快,因为(在幕后)加法和减法运算本质上是整数运算。 @SQLMenace 的示例(对不知情的人来说是一个很好的警告)同样适用于INT
egers,其结果将为零。但这不是不使用整数的理由——在适当的地方。
因此,当您处理的是 MONEY
并根据它遵循的数学规则(与 INT
eger 相同)使用它时,使用 MONEY
是完全“安全的”和合适的。
如果 SQL Server 将 MONEY
的除法和乘法提升为 DECIMAL
(或 FLOAT
?)会更好吗?可能,但他们没有选择这样做;在划分他们时,他们也没有选择将 INT
egers 提升为 FLOAT
s。
MONEY
没有精度问题; DECIMAL
在计算期间使用更大的中间类型只是使用该类型的一个“功能”(我实际上不确定该“功能”扩展了多远)。
要回答具体问题,“令人信服的理由”?好吧,如果您希望在 x
可以是 DECIMAL
或 MONEY
的 SUM(x)
中获得绝对的最大性能,那么 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
截断 - 与整数相同。)
MONEY
比 large DECIMAL
少一个字节,精度高达 19 位。但是,大多数现实世界的货币计算(高达 999 万美元)都可以放入 DECIMAL(9, 2)
,它只需要五个字节。您可以节省大小,减少对舍入错误的担心,并且使您的代码更便携。
我们刚刚遇到了一个非常相似的问题,我现在非常喜欢 +1,因为除了在顶级演示中之外从不使用 Money。由于历史原因,我们有多个表(实际上是销售凭证和销售发票),每个表都包含一个或多个 Money 字段,我们需要执行按比例计算以计算出总发票 Tax 与每个表相关的比例销售凭证上的一行。我们的计算是
vat proportion = total invoice vat x (voucher line value / total invoice value)
这导致现实世界的货币/货币计算导致除法部分的比例错误,然后乘以不正确的增值税比例。当随后添加这些值时,我们最终得到增值税比例的总和,这些总和不等于发票总价值。如果括号中的任何一个值都是小数(我将要转换其中一个),那么增值税比例将是正确的。
当括号最初不存在时,这曾经起作用,我猜是因为涉及的值更大,它有效地模拟了更高的比例。我们添加了括号,因为它首先进行乘法运算,这在极少数情况下会破坏可用于计算的精度,但现在这导致了这个更常见的错误。
作为对其他答案的总体主旨的反驳。请参阅 金钱的诸多好处...数据类型! SQLCAT's Guide to Relational Engine
具体来说,我会指出以下几点
在处理客户实现时,我们发现了一些关于货币数据类型的有趣的性能数据。例如,当 Analysis Services 设置为货币数据类型(从双精度)以匹配 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的Tabular Data Stream(TDS)协议,该协议具有关键设计原则以紧凑的二进制形式传输数据,并尽可能接近 SQL Server 的内部存储格式。根据经验,这在 SSIS 2008 中观察到 - 使用 Kernrate 的世界纪录 ETL 性能测试;当数据类型从十进制转换为货币时,协议显着下降。这使得数据传输尽可能高效。与固定宽度类型相比,复杂数据类型需要额外的解析和 CPU 周期来处理。
所以这个问题的答案是“这取决于”。您需要对某些算术运算更加小心以保持精度,但您可能会发现性能方面的考虑让这很值得。
我想对 MONEY 与 NUMERICAL 给出不同的看法,主要基于我自己的专业知识和经验......我的观点是 MONEY,因为我已经使用它很长时间了,并且从未真正使用过 NUMERICAL.. .
钱临:
本机数据类型。它使用与 CPU 寄存器(32 位或 64 位)相同的本机数据类型(整数),因此计算不需要不必要的开销,因此它更小更快...... MONEY 需要 8 个字节和 NUMERICAL(19, 4 ) 需要 9 个字节(大 12.5%)... MONEY 更快,只要它被用于它的本意(作为金钱)。多快?我对 100 万条数据的简单 SUM 测试表明,MONEY 是 275 毫秒,NUMERIC 是 517 毫秒……这几乎是两倍的速度……为什么要进行 SUM 测试?查看下一个专业点
最适合钱。 MONEY 最适合存储资金和进行操作,例如会计。在 SUM 操作完成后,单个报表可以运行数百万次加法 (SUM) 和几次乘法。对于非常大的会计应用程序,它的速度几乎是原来的两倍,而且非常重要……
货币精度低。现实生活中的金钱不需要非常精确。我的意思是,很多人可能关心 1 美分美元,但 0.01 美分美元呢?事实上,在我国,银行不再关心美分(小数点逗号后的数字);我不知道美国银行或其他国家...
金钱骗局:
有限的精度。 MONEY 只有四位数(在逗号之后)的精度,所以在进行除法等运算之前必须对其进行转换……但话又说回来,金钱不需要那么精确,它的意思是用作金钱,而不仅仅是一个号码...
但是...很大,但是这里甚至是您的应用程序涉及真实货币,但不要在很多 SUM 操作中使用它,例如在会计中。如果您使用大量除法和乘法,那么您不应该使用 MONEY...
money
列中
之前的所有帖子都带来了有效的观点,但有些帖子并没有准确回答这个问题。
问题是:当我们已经知道它是一种不太精确的数据类型并且如果用于复杂的计算会导致错误时,为什么有人会更喜欢它呢?
当您不会进行复杂的计算并且可以用这种精度来满足其他需求时,您就会使用金钱。
例如,当您不必进行这些计算,并且需要从有效的货币文本字符串中导入数据时。此自动转换仅适用于 MONEY 数据类型:
SELECT CONVERT(MONEY, '$1,000.68')
我知道您可以制定自己的导入程序。但有时您不想重新创建具有全球特定语言环境格式的导入例程。
另一个例子,当您不必进行这些计算(您只需要存储一个值)并且需要保存 1 个字节(money 需要 8 个字节,而 decimal(19,4) 需要 9 个字节)时。在某些应用程序(快速 CPU、大 RAM、慢 IO)中,例如读取大量数据,这也可以更快。
当您需要对值进行乘法/除法时,您不应该使用金钱。货币的存储方式与存储整数的方式相同,而十进制存储为小数点和小数位。这意味着货币在大多数情况下会降低准确性,而小数只有在转换回其原始比例时才会这样做。金钱是定点的,所以它的规模在计算过程中不会改变。但是,由于它在打印为十进制字符串时是固定点(而不是作为以 2 为底的字符串中的固定位置),因此可以精确表示高达 4 的值。所以对于加法和减法,钱是可以的。
小数在内部以 10 为基数表示,因此小数点的位置也基于以 10 为基数的数字。这使得它的小数部分准确地代表了它的价值,就像金钱一样。不同之处在于 decimal 的中间值可以保持高达 38 位的精度。
对于浮点数,该值以二进制形式存储,就好像它是一个整数一样,十进制(或二进制,嗯)点的位置是相对于表示该数字的位的。因为它是二进制小数点,所以以 10 为基数的数字在小数点后失去精度。 1/5 或 0.2 不能以这种方式精确表示。金钱和小数都不受此限制。
很容易将货币转换为十进制,执行计算,然后将结果值存储回货币字段或变量中。
从我的 POV 来看,我希望发生在数字上的事情会发生,而不必考虑太多。如果所有计算都将转换为十进制,那么对我来说,我只想使用十进制。我会保存钱字段以用于显示目的。
从尺寸上看,我没有看到足以改变主意的差异。 Money 占用 4 - 8 个字节,而十进制可以是 5、9、13 和 17。这 9 个字节可以覆盖 8 个字节的 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
十进制结果 > 1.000000
金钱结果 > 0.9999
浮点结果 > 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份进行计算的应用场景,但是这个例子确实暴露了一些限制。
money
和 smallmoney
数据类型精确到它们所代表的货币单位的千分之一。»正如 msdn.microsoft.com/en-us/library/ms179882.aspx 中所说,任何说“这是垃圾”的人都不知道他在说什么。
money
对money
的乘法和除法之外,这个“插图”是可操纵的。有据可查的事实是money
具有固定且非常有限的精度。在这种情况下,应该先乘,然后除。在这个例子中改变运算符的顺序,你会得到相同的结果。money
本质上是一个 64 位的 int,如果要处理 int,则在除法之前先进行乘法运算。