我很好奇money
数据类型与decimal(19,4)
之类的数据之间是否存在真正的差异(我认为这是内部使用的资金)。
我知道money
特定于SQL Server。我想知道是否有令人信服的理由选择一个而不是另一个;大多数SQL Server示例(例如AdventureWorks数据库)使用money
而不是decimal
来获取价格信息等内容。
我应该继续使用money数据类型,还是使用decimal来获益? Money输入的字符较少,但这不是一个正当理由:)
答案 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
类型为整数
smallmoney
和decimal(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
在计算过程中使用更大的中间类型只是使用该类型的一个“特征”(我实际上并不确定'特征'延伸到多远)。
回答具体问题,一个“令人信服的理由”?好吧,如果您希望在DECIMAL
中MONEY
可以是MONEY
或SMALLMONEY
的绝对最高性能,则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 Con:
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')