让我说一个明显的拳头:我完全理解浮点类型不能准确地表示十进制值。这不是那个!然而,浮点计算应该是确定性的。
既然这已经不碍事了,让我向您展示我今天观察到的奇怪案例。我有一个浮点值列表,我想总结一下:
CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);
SELECT STR(SUM(#someFloats.val), 30, 15) FROM #someFloats;
DROP TABLE #someFloats;
-- yields:
-- 13.600000000000001
到目前为止,一切都很好 - 这里没有惊喜。我们都知道1.2
不能用二进制表示精确地表示,所以“不精确”的结果是意料之中的。
现在,当我左加入另一个表时,会发生以下奇怪的事情:
CREATE TABLE #A (a int);
INSERT INTO #A (a) VALUES (1), (2);
CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);
SELECT #A.a, STR(SUM(#someFloats.val), 30, 15)
FROM #someFloats LEFT JOIN #A ON 1 = 1
GROUP BY #A.a;
DROP TABLE #someFloats;
DROP TABLE #A;
-- yields
-- 1 13.600000000000001
-- 2 13.599999999999998
(sql fiddle,你也可以在那里看到执行计划)
我对相同的值有相同的总和,但浮点错误不同。如果我向 table 添加更多行,我们可以看到该值在这两个值之间交替。我只能用;重现这个问题。在这里按预期工作。#A
LEFT JOIN
INNER JOIN
这很不方便,因为这意味着 a DISTINCT
, GROUP BY
orPIVOT
将它们视为不同的值(这实际上是我们发现这个问题的方式)。
显而易见的解决方案是对值进行舍入,但我很好奇:这种行为是否有合乎逻辑的解释?
实际上,您所指的链接并没有说浮点算术计算总是确定性的。事实上,在其中一个答案中提到了加法不是关联的(意思
(a + b) + c
不一定等于a + (b + c)
),这也在这个答案中说。如果流聚合碰巧以不同的顺序处理每个组的行——SQL Server 通常可以自由地这样做;如果
ORDER BY
在适当的子句中没有,那么优化器将选择最快的扫描或查找或其他查询运算符,而不管执行添加的顺序是什么——那么这可以解释您观察到的行为。加法总是确定性的:你放入相同的两个浮点数,你得到相同的浮点数。但是以不同的顺序将浮点数相加会产生不同的结果。