SQL Server SUM和UNION失去小数精度

时间:2016-12-15 21:39:04

标签: sql-server entity-framework

这是一个与Entity Framework和SQL Server(2016)相关的问题。

我在SQL服务器中发现了一些奇怪的行为,当将小数列与较低的比例和精度,并与另一个具有更高规模和/或更高的十进制列联合精确。结果值都具有较低的比例和精度。

这似乎不正确,因为this page表明当UNIONing时,精度将被调整为最适合它的精度。

使用此示例可以很容易地看出问题:

create table low_scale (val decimal(13,2))
create table high_scale (val decimal(19,8))

insert into low_scale values (10.00), (2.23)
insert into high_scale values (0.0000002), (2.02302023)

-- Query 1: Works fine - Result is at the widest precision required to accomodate the two types:
select * from low_scale
union all 
select * from high_scale

-- Query 2: Strange - result is rounded to two decimal places:
select sum(val) from low_scale
union all 
select sum(val) from high_scale

正如您所期望的那样,查询1的结果是:

10.00000000
2.23000000
0.00000020
2.02302023

但是,这是查询2的结果:

12.23
2.02

看来我可以通过首先将低精度列转换为更高的精度来解决这个问题,如下所示:

-- Result is at the expected 8 decimal places:
select sum(CAST(val as decimal(19,8))) from low_scale
union all 
select sum(val) from high_scale

但是,我正在使用Entity Framework 6,并且无法控制SQL生成的内容。有没有办法强制Entity Framework在执行SUM时强制转换为更高的精度,或者其他一些方法来确保正确的行为?

编辑: 我不确定为什么这被标记为浮点问题的副本。这与浮点无关 - 它使用十进制 - 一种定点数据类型。

1 个答案:

答案 0 :(得分:2)

扩展Martin Smith在评论中提到的内容,这就是正在发生的事情:

sql server documentation on SUM开始,decimal(p, s)类型的SUM结果始终为decimal(38, s)

因此,UNION前半部分的输入为decimal(38, 2),下半部分为decimal(38, 8)

This page表示当UNIONing时,结果精度为max(s1, s2) + max(p1-s1, p2-s2),且比例为max(s1, s2)

因此,将s1 = 2s2 = 8p1, p2 = 38放入其中,我们得到的精度为44,等级为8。

然而,相同页面的状态结果精度和比例的绝对最大值为38.当结果精度大于38时,相应的比例会减小,以防止结果的整数部分被截断。< / em>的

因此,它将比例缩小了6(降低到2),使总精度降低到38。

这解释了行为,但没有提供解决方案。 也就是说,有选择:

  1. 将UNION的两半作为单独的查询发出。此时我们将处理.Net十进制数,我们可以.Conact()而不必担心SQL行为。
  2. 保持所有小数类型的精度相同 - 但在存储到数据库时始终适当地舍入。
  3. (讨厌) - 在调用Sum()之前将.Conact() or .Union()的结果转换为浮点数或双精度数。显然,这引入了许多其他问题,但在某些情况下它可能是可行的。
  4. 在我的情况下,我可能会选择#2,因为肯定会有其他地方将这两个表格相加并且&amp;联合,我不希望其他devleopers认识到他们会受到这种SQL服务器行为的影响(从EF的角度来看,我们正在处理.Net小数)。