我很好奇money数据类型和decimal(19,4)之类的数据类型之间是否存在真正的区别(我相信这是金钱在内部使用的)。

我知道money是特定到SQL Server。我想知道是否有充分的理由选择一个而不是另一个?大多数SQL Server示例(例如AdventureWorks数据库)都使用money而非decimal来获取价格信息。

我应该继续使用money数据类型,还是使用十进制有好处?钱输入的字符更少,但这不是正当的理由:)

评论

DECIMAL(19,4)是一个受欢迎的选择,请在此处检查此货币,然后在此处检查世界货币格式,以确定要使用的小数位数,希望有帮助。

我想知道为什么money数据类型有4个小数..而不是2个小数,即1美元等于100美分,所以只需要2个小数位?为了存储少于9999.99美元的金额记录,我将使用数据类型为decimal(6,2)。我不关心除法或乘法运算,只存储和求和..

某些货币被分成比数百小的部分,即巴林第纳尔被分成1000 fils

#1 楼

绝对不要用钱。它不是精确的,它是纯垃圾。总是使用十进制/数字。 >对于某些表示您不会用金钱除以金钱的人:

这是我计算相关性的查询之一,将其更改为金钱会得出错误的结果。 br />
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


评论


“从不”是一个有力的词。 “ Money”对于将结果转换为该类型以便以对文化敏感的方式显示给用户非常有用,但是您没错,将其用于计算本身非常不好。

–乔尔·科恩(Joel Coehoorn)
09年2月24日在18:03

您的示例没有意义,因为没有人会相乘金钱类型的两个对象。如果要证明自己的观点,则需要比较将货币乘以小数与将十进制乘以小数。

–布赖恩
09年2月24日在18:03

..但是,为什么金钱*金钱没有金钱的准确性仍然令人困惑。

–学习
09年2月24日在18:11

@Learning:它确实有一定的金钱精度。但是,最终仍然会产生四舍五入的错误,这些错误会随着时间的推移而累积。十进制类型不使用二进制算术:它保证它得到与在纸上执行的结果相同的基数10。

–乔尔·科恩(Joel Coehoorn)
09年2月24日在19:36

除了钱的乘数和除法之外,这种“例证”是可操纵的。有据可查的事实是,金钱具有固定且非常有限的精度。在这种情况下,应首先相乘,然后相除。在此示例中,更改运算符的顺序将获得相同的结果。货币本质上是64位整数,并且如果要处理整数,则在除数之前会相乘。

– Andriy M
2011-2-8 13:34



#2 楼

SQLMenace说钱不准确。但是,您不会将金钱乘以/除以金钱! 3美元乘50美分多少钱? 150美分?您将货币乘以/除以标量,应为小数。标量应为小数。 br /> money很好,只要您不需要超过4个十进制数字,并且您可以确保不代表金钱的标量为decimal s。

评论


一美元钞票能给您多少个1美分的硬币?一个答案需要金钱。

–学习
09年2月24日在18:23

@在那种情况下学习,结果不是金钱,而是浮游物。 (基本尺寸分析。)

–理查德
09年2月24日在18:50

@Learning:您问数据库一美元多少钱吗?无论如何,那将返回正确的结果。他的问题是货币/货币只能精确到四位数字(0.2949),然后乘以10000,则变成2949.0000。

–配置器
09年2月24日在22:18

@mson他是正确的,不会像您根据一行评论所做的那样进行个人批评,这没有帮助。如果他未除以0.01美元,而是除以0.01,那么结果就是100美元而不是100。1美元中有100美分,而不是100美分。单位很重要!绝对有一个潜水的地方,也许是一钱一钱地相乘。

– andrewb
2013年6月30日23:36

如果我想花1000美元购买价格为36美元的股票,那不是合法的货币使用方式吗?答案是没有附加美元符号的整个单位,这是正确的!这是一个非常合理的场景,表明由于类似这样的奇怪情况,金钱不是一个好用的数据类型,因为结果总是被截断以适合金钱,而不是遵循精度和规模的常规规则。如果要计算一组货币值的标准偏差怎么办?是的,Sqrt(Sum(money * money))(简体)实际上确实有意义。

– ErikE
2014年6月12日22:05

#3 楼

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

即使是高精度的十进制类型也无法保存一天: > 1.000000 <-应该为0.6000000
money类型是整数可互换的。当您看到日期存储为smallmoney时,您会畏缩吗?这是同一回事。

在幕后,decimal(10,4) / varchar(10)只是money / smallmoney bigint文本表示形式中的小数点是视觉绒毛,就像yyyy-mm- dd日期。 SQL实际上并没有在内部存储这些内容。
关于intmoney,请选择适合您需要的内容。之所以存在decimal类型,是因为将计费值存储为单位的1/10000的整数倍非常常见。另外,如果您要处理的是实际的金钱和计算,而不仅仅是简单的加法和减法,那么您不应该在数据库级别上这样做!使用支持Banker's Rounding(IEEE 754)的库在应用程序级别进行操作

评论


您能否解释一下此示例中发生的情况?

–谢尔盖·波波夫(Sergey Popov)
2014年5月15日7:17

规模溢出。在小比例尺上,numeric(a,b)* numeric(c,d)产生numeric(a-b + c-d + 1,max(b,d))。但是,如果(a + b + c + d)> 38,SQL会限制小数位数,从小数点一面掠夺精度到填充整数一面,从而导致舍入错误。

–阿农
2014年5月15日20:25

所有数字计算都容易因缩放而损失精度:而是计算select 1000000 * @ num1 * @ num2

–米奇小麦
2014年7月21日在4:54



Anon的示例是特定于版本和数据库的。但要注意一点是有效的。在sqlanywhere 1.1版中,该示例确实给出了0.600000。 (我知道我们在这里谈论ms sql)。关于其他数据库中缺少货币类型的另一点,有一些方法可以将十进制或数字称为货币,例如创建域。

–gg89
2014年9月19日下午5:57

我认为这是最有启发性的答案,因为您不仅解释了所传播的错误,而且还解释了幕后真正发生的事情以及为什么MONEY首先存在。我不确定为什么要花4年的时间才能得到这个答案,但这也许是因为对计算“问题”的关注过多,而不是金钱与小数的原因和方式。

– Steve Sether
16年5月3日在16:24

#4 楼

我意识到WayneM曾说过他知道金钱是特定于SQL Server的。但是,他问是否有任何理由要使用十进制以上的货币,反之亦然,我认为仍然有一个明显的原因应该指出,那就是使用十进制意味着不必更改DBMS就少了一件事情-可能发生。

使您的系统尽可能灵活!

评论


可能但在理论上,您可以在另一个DBMS中声明一个名为Money的域(该域支持域的声明)。

–战斗机
15年7月13日在14:12

作为当前将SQL Server数据库转换为Amazon Redshift的人,我可以保证这是一个真正的问题。除非特殊的商业理由要使用,否则请尽量避免使用特定于数据库平台的数据类型。

–内森·格里菲思(Nathan Griffiths)
17年5月30日在23:12

@Nathn给出了Redshift与PostgreSQL或SQL Server相比的弱类型系统,例如没有日期类型,我想说你总是会遇到这种问题。您应该说“当数据库已经提供了更好,更符合标准的类型并警告不赞成使用的类型时,请不要使用不赞成使用的类型”。

– Panagiotis Kanavos
17年7月21日在7:44



@PanagiotisKanavos-钱不被弃用

–马丁·史密斯
17年7月21日在7:52

@MartinSmith对此感到震惊,因为它实际上无法处理2017年的BTC之类的货币价值。或者区分英镑和欧元金额-即使它们趋于平价:P。无论如何,迁移到Redshift会带来很多痛苦

– Panagiotis Kanavos
17年7月21日在8:23

#5 楼

好吧,我喜欢MONEY!它比DECIMAL便宜一个字节,并且计算速度更快,因为(在幕后)加法和减法运算本质上是整数运算。 @SQLMenace的示例(对未意识到的用户发出的警告是个不错的警告)可以同样应用于INT egers,其结果为零。但这并没有理由在适当的地方不使用整数。

因此,当您要处理的是MONEY并根据其遵循的数学规则使用MONEY时,这绝对是“安全的”且适当的选择(与INT eger相同)。

如果可能的话,SQL Server可以将MONEY的除法和乘法提升为DECIMAL(或FLOAT?),这会更好,但是,他们没有选择做这个;在划分它们时,他们也没有选择将INT egers提升到FLOAT s。 MONEY在计算过程中使用较大的中间类型只是使用该类型的“功能”(我实际上不确定“功能”的扩展范围)。

要回答具体问题,一个“令人信服的理由”?好吧,如果您想在DECIMAL中获得绝对最大的性能,其中SUM(x)可以是xDECIMAL,那么MONEY将具有优势。

也不要忘记它的表弟MONEY较小,仅4个字节,但确实在SMALLMONEY处达到最大值-这对于金钱来说是很小的钱,因此通常不太适合。

要证明使用较大的中间类型的意义,如果您将中间明确地分配给变量214,748.3647遇到相同的问题:

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


产生DECIMAL(好的,因此至少将2950.0000舍入而不是四舍五入到DECIMAL,与整数相同) />

评论


MONEY比大DECIMAL少一个字节,最高精度为19位。但是,大多数现实世界中的货币计算(不超过9.99万美元)都可以放在DECIMAL(9,2)中,它只需要五个字节。您可以节省大小,不用担心舍入错误,并使代码更具可移植性。

–user565869
2014年6月30日15:56

尽管@JonofAllTrades是正确的,但您也可以通过简单地使用包含几美分或美分的整数来获得整数性能,并保存编码小数点位置的多余字节,在将小数相加在一起时必须进行检查。

–dsz
15年6月29日在23:30

“因此,当您处理的是货币时,并根据其遵循的数学规则使用货币,这是绝对'安全'且适当的选择。”->但是,另请参阅阿农的回答:“如果不这样做,一切都是危险的知道你在做什么“。您永远无法预测人们最终将如何查询正在创建的系统,最好是避免在可能的情况下误用的可能性。微小的存储收益不值得恕我直言。

–内森·格里菲思(Nathan Griffiths)
17年5月31日在0:06

尝试存储比特币值。它有8位小数

– Panagiotis Kanavos
17年7月21日在7:49

#6 楼

我们刚刚遇到了一个非常相似的问题,除了顶级演示文稿之外,我现在从未使用过Money就是+1。由于历史原因,我们有多个表(实际上是一张销售凭单和一张销售发票),每个表包含一个或多个Money字段,并且我们需要执行按比例计算,以计算出与每个发票相关的总发票税额是多少行在销售凭证上。我们的计算结果是

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


这将导致真实的货币/货币计算,这会导致划分部分的比例误差,然后乘以错误的增值税比例。随后添加这些值时,我们得出的增值税比例之和不等于发票总值。如果括号中的任何一个值都是小数点(我将这样强制转换其中一个),那么增值税比例将是正确的。我认为,由于涉及更大的价值,因此可以有效地模拟更高的规模。我们添加了括号,因为它首先进行了乘法运算,这在极少数情况下会降低计算的精度,但这现在导致了这个更常见的错误。

评论


但这之所以如此,是因为无知的开发人员忽略了增值税计算规则和已记录的货币精度限制。

– TomTom
2014年9月16日15:27

@TomTom,但您要指出的是滥用此数据类型的可能性,这是一个主张完全避免使用它的参数。

–内森·格里菲思(Nathan Griffiths)
17年5月31日在0:11

@Nathan否。我指出,作为永远不使用它的理由而给出的论点基本上是无能的开发人员,因此论点是虚假的。

– TomTom
17年5月31日下午6:26

@TomTom可悲的是,有很多不称职的开发人员(以及许多出色的开发人员),对我而言,除非我可以保证将来会如何查询这些数据,而且没有人会犯这里描述的那种错误,最好还是谨慎一点,并使用十进制类型。就是说,阅读了所有这些答案之后,我看到有一些特定的用例,其中金钱是使用的最佳类型,除非有很好的用例,否则我不会使用它(例如,列只会用到由SUM汇总,批量加载大量财务数据)。

–内森·格里菲思(Nathan Griffiths)
17年6月1日在1:33

@TomTom不仅仅是精度限制。内部表示形式也会引起问题。金钱是一种方便的类型,可以抓住无知的开发人员,而不是开发人员滥用的出色类型。增值税示例,无论其计算规则如何,显然都应在将具体知识应用到通用工具时吓退那些无知的开发人员。

– Gerard ONeill
18 Mar 17 '18 at 0:51

#7 楼

作为对其他答案总推论的对策。请参阅金钱的许多好处…数据类型!在SQLCAT的《关系引擎指南》中,

我特别指出以下内容


在处理客户实现时,我们发现了一些有趣的关于货币数据类型。例如,将
Analysis Services设置为货币数据类型(从双精度)到
与SQL Server货币数据类型匹配时,
处理速度(行/行)提高了13%。秒)。为了在SQL
服务器集成服务(SSIS)中获得更快的性能,以在30分钟之内加载1.18 TB的数据,如SSIS 2008中所述-世界纪录ETL性能,
观察到TPC-H LINEITEM表中的4个十进制(9,2)列,其大小为5
字节,转换为money(8字节),使批量
插入速度提高了20%...性能的原因改进是由于SQL Server的表格数据流(TDS)协议,该协议的主要设计原理是以紧凑的二进制形式传输数据,并尽可能接近SQL Server的内部存储格式。根据经验,这是在SSIS 2008(使用Kernrate的ETL性能世界纪录测试)中观察到的。当数据类型从十进制转换为金钱时,协议显着下降。这样可以使数据传输尽可能高效。与固定宽度类型相比,复杂的数据类型需要更多的解析和CPU周期来处理。您需要对某些算术运算更加谨慎,以保持精度,但您可能会发现,出于性能考虑,这值得。

评论


那你将如何存储比特币?

– Panagiotis Kanavos
17年7月21日在7:50

@PanagiotisKanavos-我不知道。我从未研究过比特币,对此一无所知!

–马丁·史密斯
17年7月21日在7:54

链接无效。这是PDF的链接download.microsoft.com/download/0/F/B/…

–vcs
12月4日3:49

#8 楼

我想根据我自己的专业知识和经验给出不同的货币与数值观点...我的观点是货币,因为我已经使用货币很长时间了,而且从没有真正使用数字。 。

MONEY Pro:



本地数据类型。它使用与CPU寄存器(32或64位)相同的本机数据类型(整数),因此计算不需要不必要的开销,因此它更小,更快... MONEY需要8个字节和NUMERICAL(19,4 )需要9个字节(较大的12.5%)...多快?我对100万个数据进行了简单的SUM测试,结果显示MONEY为275毫秒,NUMERIC 517毫秒。这几乎快了一倍...为什么进行SUM测试?请参阅下一个Pro point


最佳性价比。金钱最适合用于存储金钱和进行操作,例如会计。在完成SUM操作之后,单个报告可以运行数百万个加法(SUM)和几个乘法。对于非常大的会计应用程序,它的速度几乎快一倍,而且意义非凡...

货币精度低。现实生活中的金钱并不需要非常精确。我的意思是,很多人可能会关心1美分,但是0.01美分呢?实际上,在我国,银行不再关心美分(小数点后的数字)。我不了解美国银行或其他国家/地区...

钱骗子:



有限精度。 MONEY仅具有四位数字(在逗号后)精度,因此必须在进行除法运算之前将其转换...但是,再次money并不需要那么精确,并且可以用作货币,而不仅仅是一个数字...

但是...很大,但这甚至是您的应用程序涉及到真实货币的情况,但是不要在很多SUM操作中使用它,例如在会计中。如果您使用大量除法和乘法运算,则不应使用MONEY ...

评论


如果您要说钱比十进制要快,那么您需要告诉我们诸如rdbms,在什么硬件上,在什么OS上运行以及在什么特定数据在运行什么特定查询的事情。此外,如果它不完全准确,则我不会对其进行任何财务操作,因为税务员会很不高兴拿回坏帐。例如,如果您使用美元交易,则需要支付千分之一的费用。如果没有钱,那就没用了。

– HaakonLøtveit
17年1月16日在13:52

@HaakonLøtveit整个问答环节都涉及SQL Server数据类型“ money”,因此我认为他们不需要在答案中指定此内容。在某些情况下(例如加载数据),金钱可以比十进制更快地使用,请参阅马丁史密斯的答案以获取更多详细信息。我同意大部分答案,因为在某些情况下,使Money成为比小数点更为有效的选择,但是,除非有非常令人信服的用例,否则我认为应避免使用。

–内森·格里菲思(Nathan Griffiths)
17年5月31日在0:17

比特币有8位小数。您甚至无法将其存储在钱栏中

– Panagiotis Kanavos
17年7月21日在7:51

@HaakonLøtveit..好吧,我想说的是,在体系结构级别上,本机类型总是在性能方面再次赢得非本机类型的胜利,而本机类型则取决于体系结构,例如在CPU(整数类型),FPU float和在GPU上是float数组,依此类推。

–苏西洛
12月10日5:18

#9 楼

以前的所有文章都提出了有效的观点,但是有些文章并没有给出准确的答案。如果用于复杂的计算?

当您不进行复杂的计算时会用钱,并且可以将此精度用于其他需求。必须进行这些计算,并且需要从有效的货币文本字符串中导入数据。此自动转换仅适用于MONEY数据类型:

SELECT CONVERT(MONEY, ',000.68')


我知道您可以创建自己的导入例程。但是有时您不想使用全球特定的语言环境格式重新创建导入例程。

另一个示例,当您不必进行这些计算(您只需要存储一个值)并且需要保存1个字节(金钱占用8个字节,十进制(19,4)占用9个字节)。在某些应用程序(快速CPU,大RAM,慢速IO)中,例如仅读取大量数据,这也可以更快。

#10 楼

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

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

使用浮点数时,该值以二进制形式存储,就像是整数一样,十进制(或二进制,点)的位置相对于代表数字的位。因为它是二进制小数点,所以基数10的数字在小数点后立即失去精度。用这种方式无法精确表示1/5或0.2。钱和十进制都不受此限制。

将钱转换为十进制,执行计算,然后将结果值存储回money字段或变量中是很容易的。 br />从我的观点来看,我希望数字中发生的事情能够发生而不必考虑太多。如果所有计算都将转换为十进制,那么对我来说,我只想使用十进制。我将把钱字段保存起来以供显示。

从大小上看,我没有太大的改变可以改变主意。 Money占用4-8个字节,而十进制可以是5、9、13和17。这9个字节可以覆盖8个bytes货币可以覆盖的整个范围。按索引编制(比较和搜索应具有可比性)。

评论


感谢您的支持。我正在看这个,当我明白我想说的话时,我也看到它很混乱。也许以后再用一些小数示例来重写它。

– Gerard ONeill
18 Mar 17 '18 at 0:39

#11 楼

我找到了在精度主题中使用小数而不是金钱的原因。 />
FloatResult> 1

只需测试一下并做出决定即可。

评论


我们非常想听听(读到)您的结论。

– Peter Mortensen
18年7月21日在16:21

@PeterMortensen我想如果要在Money和Decimal类型之间具有完整性和准确性,我的决定应该是Decimal。

– QMaster
18年8月20日在8:51

考虑使用上述实际结果更新您的答案。那么您的结论对于阅读:-)的任何人都会立即显而易见。

– SOS
19-09-14在18:18



@Ageax将结果添加到答案中。

– QMaster
19-09-25在10:47

#12 楼

我刚刚看到了这个博客文章:SQL Server中的资金与小数。 />对于money类型,您将获得19.30而不是19.34。我不确定是否存在将钱分为1000个部分进行计算的应用场景,但是此示例确实暴露了一些限制。

评论


这不是“问题”,而是“按规格”:“ Money和s​​mallmoney数据类型精确到代表的货币单位的千分之一。”如msdn.microsoft.com/en-us/library/ms179882.aspx中所述,因此任何说“这是垃圾”的人都不知道他在说什么。

– Albireo
2012年7月5日在12:40