我今天的会议太多了,但我想我的脑软件仍然存在。 在我努力提高某些查询的性能时,我遇到了以下谜团(表名和字段释义):
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:此查询在另一个函数内部调用,因此将结果放在临时表中,然后进一步过滤它们也是不可能的。
答案 0 :(得分:0)
就像说明一样,优化器不会像您那样读取查询。即使您认为应该发生某个顺序,或者短路可能最有意义,优化器仍然可能按照您可能不期望的顺序评估CTE /子查询。您可能尝试的解决方法是在#temp表中选择第一个查询,然后在#temp表上运行函数过滤器。这应该强制评估的顺序,即使它完全不直观,也不那么优雅。
修改强>
此外,虽然它可能执行速度较慢,但我很好奇如果你在没有NOLOCK的情况下运行查询,或者在RCSI中运行。不同的锁定语义可能会使优化器绊倒。
答案 1 :(得分:0)
我们将此问题提交给Microsoft对SQL Server R2的支持(我必须评论他们惊人的响应时间和整体服务程序)。我们给了他们一份我们的数据库副本来重现这个问题,我们的解决方法,他们自己复制了,几天后我们得到了答案:
我已经分析了两个执行计划,并且会好意地问一下 在生产中使用变通方法是否可以接受?主要原因 在它背后,是一个函数没有,正如索引所有, 统计。这种缺乏数据使优化器有时会选择 一个不太好的执行计划。如果您已找到解决方法,那就是 最好实现这一点。我们尝试的指数变化没有改善 执行。
这是一种非常外交的方式来说“是的,优化程序会使您的查询混乱,所以请使用解决方法”。如果您想将其称为错误,请将其称为错误,这无关紧要。
仅为记录,解决方法是将调用fn_B()放在SELECT DISTINCT上一级查询的SELECT列表中,然后在WHERE条件上过滤其结果。有点奇怪,但它确实有把戏。