那我应该使用什么?
那我应该使用什么?
不,货币仍然应该有效。
Papuccino,
我不建议使用money和smallmoney类型,除非你确定你只需要进行加减法运算。如果你可能会处理汇率、百分比等问题,使用这些类型会导致真正的问题。
这里只是举了一个小例子来展示在涉及除法时使用money、decimal和float之间的差异。可能会出现差异更明显的例子。
declare @m1 money, @m2 money, @m3 money
declare @d1 decimal(19,4), @d2 decimal(19,4), @d3 decimal(19,4)
declare @f1 float, @f2 float, @f3 float;
set @m1 = 1.00;
set @m2 = 345.00;
set @m3 = @m1/@m2;
set @d1 = 1.00;
set @d2 = 345.00;
set @d3 = @d1/@d2;
set @f1 = 1.00;
set @f2 = 345.00;
set @f3 = @f1/@f2;
select @m3, @d3, @f3;
结果:0.0028 0.0029 0.00289855072463768
根据所处行业,可能存在指南或规定来帮助您决定正确的数据类型。没有唯一正确的答案。
附加说明:
您是正确的,money/money 不应该是金钱,但是 SQL Server(莫名其妙地)产生了两个金钱值商的金额结果,这是错误的。尽管它毫无意义,但正如下面的示例所示,您将得到这样的结果:
declare @m1 money, @m2 money;
declare @d1 decimal(19,4), @d2 decimal(19,4);
set @m1 = 1.00;
set @m2 = 345.00;
set @d1 = 1.00;
set @d2 = 345.00;
select @m1/@m2, @d1/@d2
结果:0.0028 0.0028985507246376811
类型为money的结果0.0028比正确结果低了3-4%。
当然,有许多情况需要对货币值进行除法运算。使用money类型的危险在于商是错误的类型(并且答案与正确答案相差甚远)。需要除以货币的问题示例:
假设你兑换320元人民币,银行给你47.3美元。你得到的汇率是多少?
假设你投资23美元,一年后价值31美元。你的回报率是多少?
这两个计算都需要除以货币值。
select 9 / 10
得到的是0,这是非常错误的!然而我们一直在使用int
,你只需要理解什么是整数除法。Money
在除法过程中不改变精度,就像int
一样。因此,小数点后四位之后的数字会丢失,就像在int
中小于零的小数点后的数字一样。 - GSergDecimal
,另一方面,在除法期间使用复杂规则来改变其精度。 根据这些规则,decimal(19,4)/ decimal(19,4)
是decimal(38,19)
,它将结果存储在第19位小数点上,然后在存储在decimal(19,4)
中时会四舍五入 - 另一个关于转换精度的decimal
规则。 因此,没有什么可说money
存在问题而decimal
不存在问题。 它们只是不同的。 - GSerg我同意把钱除以钱是无意义的。但是把钱除以天数是真实的。如果你正在将少量的钱按天数分配,你需要注意这种现象。我将钱转换为浮点数,进行计算,最终将结果存储到货币数据类型中。希望这能帮到你。
令我惊讶的是之前没有人提到过。
money
占用 8 字节。
decimal
占用 5、9、13 或 17 字节,具体取决于精度(精度不是小数点后的位数,而是将存储的最大十进制数字总位数,包括小数点左右两侧)。因此,要模仿 money
支持的值范围(-922,337,203,685,477.5808 至 922,337,203,685,477.5807),需要使用 decimal(19,4)
。
+-------------------+---------------+
| decimal precision | Storage bytes |
+-------------------+---------------+
| 1 - 9 | 5 |
| 10-19 | 9 |
| 20-28 | 13 |
| 29-38 | 17 |
+-------------------+---------------+
money
是一种本地处理器类型,如64位bigint
,但decimal
不是。这意味着对数十亿个money
值求和比求和decimal
值更快。执行其他计算例如除法将会有更大的差异。dbo.Numbers
是一个带有1至10,000值的int
列Number
的10,000行表。CREATE TABLE [dbo].[Numbers](
[Number] [int] NOT NULL,
CONSTRAINT [PK_Numbers] PRIMARY KEY CLUSTERED
(
[Number] ASC
))
我在SQL Sentry Plan Explorer中运行了这个:
DECLARE @VarM money = 1234.5678;
SELECT
AVG(@VarM / N1.Number)
FROM
dbo.Numbers AS N1
CROSS JOIN dbo.Numbers AS N2
;
DECLARE @VarD decimal(19,4) = 1234.5678;
SELECT
AVG(@VarD / N1.Number)
FROM
dbo.Numbers AS N1
CROSS JOIN dbo.Numbers AS N2
;
执行计划对于这两个查询是相同的(当然,Number
会有不同的隐式转换:转为 money
和 decimal
),但运行时间却是15秒和40秒。这对我来说是非常明显的。
money
只有4个小数位。如果进行计算,您需要了解类型(以及它们的优先级,即什么会被隐式转换为什么),并确保中间结果是适当的类型,如果需要,通过将操作数强制转换为正确的类型来实现。这个警告适用于任何类型的计算,不仅仅是money
。当您除以两个int
值时,应该知道结果是int
,而不会惊讶于4 / 5 = 0
。
VALUES (12.34), (10.79), (18.43)
,然后选择它们,他将看到这些值与输入的值相同,而不是12.3400, 10.7900, 18.4300
。这是一个严重的问题,因为它会让他认为“money”数据类型只存储2个小数位。然后,他执行INSERT MyTable (PurchaseAmount) SELECT Shares * PricePerShare
,而没有截断为2个小数位。糟糕!此外,“money”可以被“money”除(我想花费1000美元购买每股36美元的股票)和乘(我想计算标准偏差)。 - ErikEDeclare @m Money = 123.45
Select str(@m, 8, 4)
或者更好的方法是,试试Declare @m Money = 123.45
Select str(@m, 12, 9)
。格式化后的值与存储的值几乎没有关系... 讨论“存储”的小数位数甚至没有意义... 如果您将值2保存在类型为Numeric(12,11)的字段中,您真的认为它没有存储十一个零吗? - Charles Bretanamoney
却隐藏了这一点。我个人没有理解问题,但事实是许多软件开发公司充满了那些并不真正了解数据库的开发人员,即使如此,他们仍然是优秀的开发人员。money
是一种可怕的数据类型,因为它隐藏了掩码后面的精度和比例。你提到的numeric(12,11)
的例子并不好,因为开发人员(虽然在SQL Server方面缺乏经验,但通常很聪明)可以看到有11个小数位,这与money
不同。 - ErikE