1

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

以下查询产生零误差

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 小提琴演示

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

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 小提琴演示

通过查看估计的查询计划,第一个查询尝试将 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 声明的目的。

4

1 回答 1

1

转到此连接项目并投票。

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

至于您的查询,是的,Filtered CTE 是一个非事件,应该滚动到最终查询中。以这种方式编写它表明您相信 CTE 以某种方式处理并缓存在内存中。那不是真的-请参阅this answer to another SO question。有关 SQL Server 的 CTE 实现的扩展阅读,请访问本文

于 2012-10-22T05:07:05.507 回答