SQL Server查询性能之谜

时间:2012-05-03 16:16:50

标签: sql-server-2008 database-performance

我今天的会议太多了,但我想我的脑软件仍然存在。 在我努力提高某些查询的性能时,我遇到了以下谜团(表名和字段释义):

SELECT X.ADId FROM
(
    SELECT DISTINCT A.ADId
    FROM P WITH (NOLOCK)
    INNER JOIN A WITH (NOLOCK) ON (P.ID = A.PId)
    INNER JOIN dbo.fn_A(16) AS VD ON (VD.DId = A.ADId)
    LEFT JOIN DPR ON (LDID = A.ADId)
    WHERE ((A.ADId = 1) OR ((HDId IS NOT NULL) AND (HDId = 1))) AND
           (P.PS NOT IN(5,7)) AND (A.ASP IN (2, 3))
) X
WHERE (dbo.fn_B(X.ADId, 16) = 1)

正如您将看到的,内部查询的内容大多无关紧要。 最初的一点是我想避免在每个记录上调用fn_B()因为它们包含ADId的重复值,所以我在内部做了一个SELECT DISTINCT然后过滤不同的记录。 听起来合理吗?

这里开始了神秘......

内部查询返回NO RECORDS(对于指定的参数)。 如果我注释掉“WHERE fn_B()= 1”,那么查询将在零时间内运行(并且不返回任何结果)。 如果我重新打开它,那么查询需要6-10秒,再次没有返回结果。

这似乎超越了常识,或者至少是我常见的SQL意义:-) 如果内部查询没有返回数据,那么永远不应该评估外部条件吗?

当然,我花时间检查实际执行计划,保存并仔细比较。它们是99%完全相同,没有什么不寻常的注意,或者我认为。

我骗过一些CTE来获取第一个CTE中的查询结果,然后将其传递给第二个CTE,该CTE有一些条件保证不过滤任何记录,然后评估所有CTE外的fn_B()调用,但是行为完全一样。

其他变体,例如使用旧查询(可能多次使用相同值调用fn_B())具有相同的行为。如果我删除条件,那么我在零时间内没有记录。如果我把它放回去,那么10秒内就没有记录了。

任何想法?

感谢您的时间: - )

PS1:我尝试使用简单的查询重现tempdb上的情况,但我无法实现。它只发生在我的实际表格上。 PS2:此查询在另一个函数内部调用,因此将结果放在临时表中,然后进一步过滤它们也是不可能的。

2 个答案:

答案 0 :(得分:0)

就像说明一样,优化器不会像您那样读取查询。即使您认为应该发生某个顺序,或者短路可能最有意义,优化器仍然可能按照您可能不期望的顺序评估CTE /子查询。您可能尝试的解决方法是在#temp表中选择第一个查询,然后在#temp表上运行函数过滤器。这应该强制评估的顺序,即使它完全不直观,也不那么优雅。

修改

此外,虽然它可能执行速度较慢,但​​我很好奇如果你在没有NOLOCK的情况下运行查询,或者在RCSI中运行。不同的锁定语义可能会使优化器绊倒。

答案 1 :(得分:0)

我们将此问题提交给Microsoft对SQL Server R2的支持(我必须评论他们惊人的响应时间和整体服务程序)。我们给了他们一份我们的数据库副本来重现这个问题,我们的解决方法,他们自己复制了,几天后我们得到了答案:

  

我已经分析了两个执行计划,并且会好意地问一下   在生产中使用变通方法是否可以接受?主要原因   在它背后,是一个函数没有,正如索引所有,   统计。这种缺乏数据使优化器有时会选择   一个不太好的执行计划。如果您已找到解决方法,那就是   最好实现这一点。我们尝试的指数变化没有改善   执行。

这是一种非常外交的方式来说“是的,优化程序会使您的查询混乱,所以请使用解决方法”。如果您想将其称为错误,请将其称为错误,这无关紧要。

仅为记录,解决方法是将调用fn_B()放在SELECT DISTINCT上一级查询的SELECT列表中,然后在WHERE条件上过滤其结果。有点奇怪,但它确实有把戏。