我很好奇money
数据类型和类似的东西之间是否存在真正的区别decimal(19,4)
(我相信这是金钱在内部使用的东西)。
我知道这money
是特定于SQL Server的。我想知道是否有令人信服的理由选择一个而不是另一个?大多数SQL Server示例(例如AdventureWorks数据库)使用的money
不是decimal
价格信息。
我应该继续使用money数据类型,还是使用十进制有好处?钱是较少键入的字符,但这不是正当的理由:)
我很好奇money
数据类型和类似的东西之间是否存在真正的区别decimal(19,4)
(我相信这是金钱在内部使用的东西)。
我知道这money
是特定于SQL Server的。我想知道是否有令人信服的理由选择一个而不是另一个?大多数SQL Server示例(例如AdventureWorks数据库)使用的money
不是decimal
价格信息。
我应该继续使用money数据类型,还是使用十进制有好处?钱是较少键入的字符,但这不是正当的理由:)
Answers:
绝对不要用钱。它不是精确的,它是纯垃圾。始终使用十进制/数字。
运行此命令以了解我的意思:
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
money
以上money
之外,这个“插图”是操纵。有记录的事实money
具有固定且非常有限的精度。在这种情况下,应首先相乘,然后相除。在此示例中,更改运算符的顺序将得到相同的结果。a money
本质上是64位int,如果要处理int,则在相除之前会相乘。
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
结果正确:
货币结果数值结果 --------------------- ----------------------------- ---------- 2949.8525 2949.8525
money
只要您不需要多于4个十进制数字,并且您确保您的标量(不代表金钱)为decimal
s,就可以了。
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
vs money
,选择适合您需求的任何东西。money
存在这些类型是因为非常常见的是将计费值存储为单位的1/10000的整数倍。此外,如果您要处理的是实际的金钱和计算,而不是简单的加减运算,则不应在数据库级别执行此操作! 使用支持Banker's Rounding(IEEE 754)的库在应用程序级别执行此操作
我意识到WayneM曾说过他知道金钱是特定于SQL Server的。但是,他在问是否有任何理由要用十进制来代替金钱,反之亦然,我认为还有一个明显的理由应该指出,那就是使用十进制意味着不必更改DBMS就少了一件事情-可能会发生。
使您的系统尽可能灵活!
date
类型,我想说你总是会遇到这样的问题。您应该说“当数据库已经提供了更好,更符合标准的类型并警告不推荐使用的类型时,请不要使用不推荐使用的类型”。
好吧,我喜欢MONEY
!它比便宜1字节DECIMAL
,并且计算速度更快,因为(在幕后)加法和减法运算本质上是整数运算。@SQLMenace的示例(对不了解的用户发出警告,这是一个很好的警告)可以同样地应用于INT
结果为零的egers。但这没有理由不使用整数-在适当情况下。
因此,这是完全“安全”的,适合MONEY
在您要处理的事物上MONEY
使用并根据其遵循的数学规则(与INT
eger一样)使用它。
如果SQL Server 可能MONEY
将DECIMAL
s的除法和乘法推广到s(或FLOAT
s?),会更好,但是他们没有选择这样做。他们也没有选择提拔INT
egers提升为FLOAT
s。
MONEY
没有精度问题;那DECIMAL
得面对具有计算过程中使用较大的中间类型仅仅是使用该类型的“功能”(我不肯定实际上多远,即“功能”延伸)。
要回答具体问题,有“令人信服的理由”吗?好吧,如果你想在一个绝对最大的性能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
截断,与整数相同。)
MONEY
比大号 少1个字节DECIMAL
,最高精度为19位。但是,大多数现实世界中的货币计算(最多999万美元)都可以放入DECIMAL(9, 2)
,只需要五个字节。您可以保存大小,少担心舍入误差,并且使你的代码的可移植性。
我们刚刚遇到了一个非常相似的问题,除了顶级演示文稿之外,我现在几乎是永远不使用Money的+1。由于历史原因,我们有多个表(实际上是一张销售凭单和一张销售发票),每个表都包含一个或多个Money字段,并且我们需要执行按比例计算,以计算出与每个发票相关的总发票税额是多少行在销售凭证上。我们的计算是
vat proportion = total invoice vat x (voucher line value / total invoice value)
这导致现实世界中的金钱/金钱计算,这会导致除法部分的比例误差,然后乘以错误的增值税比例。随后添加这些值时,我们得出的增值税比例之和不等于发票总值。如果括号中的任何一个值都是小数点(我将这样强制转换其中一个),则增值税比例将是正确的。
我猜想,当最初没有括号时,它就可以工作,因为涉及的值较大,所以它实际上是在模拟更高的比例尺。我们添加了括号,因为它首先进行了乘法运算,这在极少数情况下降低了可用于计算的精度,但是现在这引起了这个更为常见的错误。
作为对其他答案总推论的对策。请参阅金钱的许多好处…数据类型!在《SQLCAT关系引擎指南》中
我具体指出以下几点
在客户实施方面,我们发现了一些有关money数据类型的有趣性能指标。例如,将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的表格数据流(TDS)协议,它具有关键设计原则,可以以紧凑的二进制格式传输数据,并尽可能接近SQL Server的内部存储格式。根据经验,在SSIS 2008(使用Kernrate的世界纪录ETL性能测试)中观察到了这一点。当数据类型从十进制转换为金钱时,协议显着下降。这使得数据传输尽可能高效。复杂数据类型比固定宽度类型需要更多的解析和CPU周期来处理。
因此,问题的答案是“取决于”。您需要对某些算术运算更加谨慎,以保持精度,但是您可能会发现,出于性能考虑,这值得这样做。
我想根据我自己的专业知识和经验给出不同的货币与数值观点...我的观点是货币,因为我已经使用货币很长时间了,并且从未真正使用过数值。 。
专业版:
本机数据类型。它使用与CPU寄存器(32或64位)相同的本机数据类型(整数),因此计算不需要不必要的开销,因此它更小,更快。 ... MONEY需要8个字节和NUMERICAL(19,4 )需要9个字节(大12.5%)...
只要用于货币(作为货币),货币就会更快。多快?我SUM
对100万个数据的简单测试显示,MONEY为275毫秒,NUMERIC 517毫秒...这几乎快了一倍 ...为什么进行SUM测试?查看下一个专业点
金钱骗局:
money
不必再那么精确了,它也可以用作货币,而不仅仅是数...但是...很大,但是您的应用程序甚至涉及真实货币,但不要在诸如会计之类的许多SUM操作中使用它。如果您使用大量除法和乘法运算,则不应使用MONEY ...
money
列中
先前的所有帖子都提供了有效的观点,但是有些帖子无法准确回答问题。
问题是:当我们已经知道钱是一种不太精确的数据类型并且如果在复杂的计算中使用会导致错误,为什么有人会喜欢钱?
当您不进行复杂的计算并且可以将这种精度用于其他需求时,便会使用金钱。
例如,当您不必进行这些计算时,需要从有效的货币文本字符串中导入数据。此自动转换仅适用于MONEY数据类型:
SELECT CONVERT(MONEY, '$1,000.68')
我知道您可以制作自己的导入例程。但是有时您不想使用全球特定语言环境格式重新创建导入例程。
另一个示例,当您不必进行这些计算(您只需要存储一个值)并且需要保存1个字节(金钱占用8个字节,而十进制(19,4)占用9个字节)时。在某些应用程序中(快速CPU,大RAM,慢速IO),例如仅读取大量数据,这也可能更快。
当您需要对值进行乘/除时,您不应该使用金钱。货币的存储方式与存储整数的方式相同,而十进制存储为小数点和十进制数字。这意味着金钱在大多数情况下会降低准确性,而十进制只有在转换回原始比例时才会这样做。货币是定点的,因此其规模在计算过程中不会改变。但是,因为当它以十进制字符串形式打印时是固定点(与以2为基数的字符串中的固定位置相对),所以精确地表示了小数位数为4的值。因此,对于加法和减法,钱是可以的。
小数点在内部以10为基数表示,因此小数点的位置也以10为基数。就像金钱一样,这使得它的小数部分准确地代表了它的价值。区别在于十进制的中间值最多可以保持38位精度。
对于浮点数,该值以二进制形式存储,就好像它是整数一样,而小数点(或二进制,ahem)点的位置相对于表示该数字的位。因为它是二进制小数点,所以以10为基数的数字在小数点后立即失去精度。用这种方式无法精确表示1/5或0.2。钱和小数都不受此限制。
将货币转换为十进制,执行计算,然后将结果值存储回money字段或变量中,这很容易。
从我的观点来看,我希望发生在数字上的事情不必花太多时间去思考。如果所有计算都将转换为十进制,那么对我来说,我只想使用十进制。我将把钱字段保存起来以供显示。
从大小上看,我没有太大的改变可以改变主意。Money占用4-8个字节,而十进制可以是5、9、13和17。这9个字节可以覆盖8个bytes货币可以覆盖的整个范围。按索引编制(比较和搜索应具有可比性)。
我找到了在准确性主题中使用小数而不是金钱的原因。
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
只需对其进行测试并做出决定。
我刚刚看到此博客文章:SQL Server中的Money 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个部分进行计算的应用场景,但是此示例确实暴露了一些限制。
money
和smallmoney
数据类型精确到它们代表的货币单位的千分之一。” 如msdn.microsoft.com/en-us/library/ms179882.aspx中所述,因此任何说“这是垃圾”的人都不知道他在说什么。
DECIMAL(19, 4)
是一个流行的选择,请在此检查并在此处检查世界货币格式,以确定要使用的小数位数,希望有所帮助。