SQL Server 2008 R2除以零错误

时间:2012-10-22 04:01:00

标签: sql-server sql-server-2008-r2

请告诉我,如果我遗失了什么,如果我失去了理智

以下查询产生零错误

DECLARE @Test TABLE(
        ID INT,
        Val FLOAT
)

INSERT INTO @Test SELECT 1, 0

;WITH   Grouped AS (
        SELECT  ID,
                Val,
                SUM(Val) OVER (PARTITION BY ID) TotalVal
        FROM    @Test
)
, Filtered AS (
        SELECT  *
        FROM    Grouped
        WHERE   TotalVal != 0
)
SELECT  *
FROM    Filtered
WHERE   Val / TotalVal > 0.05

SQL Fiddle DEMO

如下所示不会产生相同的错误

DECLARE @Test TABLE(
        ID INT,
        Val FLOAT
)

INSERT INTO @Test SELECT 1, 0

;WITH   Grouped AS (
        SELECT  ID,
                Val,
                SUM(Val) OVER (PARTITION BY ID) TotalVal
        FROM    @Test
)
, Filtered AS (
        SELECT  *
        FROM    Grouped
        WHERE   TotalVal != 0
)
SELECT  *,
        Val / TotalVal Test
FROM    Filtered

SQL Fiddle DEMO

从查看估计的查询计划,第一个查询尝试将where子句优化为

[Val]/[Expr1004]>(5.000000000000000e-002) AND [Expr1004]<>(0.000000000000000e+000)

我可以看到TotalVal != 0位于实际devide by TotalVal之后。

这是一个已知问题吗?有没有办法(除了使用另一个临时表)绕过这个。我知道我可以使用

来做到这一点
CASE WHEN TotalVal = 0 THEN 0 ELSE Val / TotalVal END

但这会破坏第二次CTE声明的目的。

1 个答案:

答案 0 :(得分:1)

转到此connect item并投票。

就个人而言,我对SQL Server的工作方式感到满意,因为它允许某种程度的优化来预先计算(简化)一个表达式而不是将组件向前推进以便稍后计算(即使它有意义逻辑)。

至于你的查询,是的,Filtered CTE是一个非事件,应该进入最终查询。以这种方式编写表明您认为CTE以某种方式处理并且缓存在内存中。事实并非如此 - 请参阅另一个问题this answer。有关SQL Server CTE实现的扩展读取,请访问this article