SQL Server 2005数值精度损失
时间:2020-03-06 14:39:13 来源:igfitidea点击:
调试一些与财务相关的SQL代码时,发现了一个数字(24,8)数学精度的奇怪问题。
在MSSQL上运行以下查询,我们将获得A + B * C表达式结果为0.123457
选择一个,
B,
C,
A + B * C
从
(
SELECT CAST(0.12345678 AS NUMERIC(24,8))AS A,
CAST(0 AS NUMERIC(24,8))AS B,
CAST(500 AS NUMERIC(24,8))AS C
)T
因此,我们丢失了2个重要符号。尝试以不同的方式解决此问题,我得到了将中间乘法结果(为零!)转换为数值(24,8)的效果。
最后有一个解决方案。但是,我仍然有一个问题,为什么MSSQL会以这种方式运行,并且样本中实际上发生了哪种类型转换?
解决方案
正如浮点类型的加法不准确一样,如果超出精度,则十进制类型的乘积可能不准确(或者导致不准确)。请参阅数据类型转换以及十进制和数字。
由于我们将NUMERIC(24,8)和NUMERIC(24,8)相乘,并且SQL Server仅检查类型而不是内容,因此它可能会尝试保存潜在的16个非十进制数字(24 8)无法保存所有48位精度时(最大为38)。将它们中的两个结合起来,我们将获得32个非十进制数字,仅剩下6个十进制数字(38 32)。
因此,原始查询
SELECT A, B, C, A + B * C FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A, CAST(0 AS NUMERIC(24,8)) AS B, CAST(500 AS NUMERIC(24,8)) AS C ) T
减少到
SELECT A, B, C, A + D FROM ( SELECT CAST(0.12345678 AS NUMERIC(24,8)) AS A, CAST(0 AS NUMERIC(24,8)) AS B, CAST(500 AS NUMERIC(24,8)) AS C, CAST(0 AS NUMERIC(38,6)) AS D ) T
同样,在NUMERIC(24,8)和NUMERIC(38,6)之间,SQL Server将尝试保存潜在的32位非十进制数字,因此A + D减小为
SELECT CAST(0.12345678 AS NUMERIC(38,6))
四舍五入后得到的值是0.123457.
按照eed3si9n指出的逻辑以及我们在问题中所说的,似乎进行数学运算时最好的方法是将它们提取到函数中,并在每次运算后指定精度,
在这种情况下,函数可能看起来像:
create function dbo.myMath(@a as numeric(24,8), @b as numeric(24,8), @c as numeric(24,8)) returns numeric(24,8) as begin declare @d as numeric(24,8) set @d = @b* @c return @a + @d end