今天我参加了太多的会议,但我认为我还有自己的智能软件。 在我的努力来改善某些查询我碰到下面的神秘来到的表现(表名和字段转述):SQL Server查询性能谜
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,然后过滤了不同的记录。 听起来合理吗?
这里开始谜...
内查询返回任何记录(对于指定的参数)。 如果我注释掉“WHERE fn_B()= 1”,那么查询在零时间运行(并且不返回结果)。 如果我把它放回去,那么查询需要6-10秒,再次返回没有结果。
这似乎打败常识,或者至少是我常见的SQL意识:-) 如果内部查询返回没有数据,那么外部条件应该永远不会被评估正确吗?
当然我花时间检查实际的执行计划,保存并仔细比较它们。它们是99%相同的,没有什么不寻常的可以注意到,或者我认为。
我愚弄了一些CTE,在第一个CTE中获取查询结果,然后将它传递给第二个CTE,该CTE有一些条件保证不过滤任何记录,然后在所有CTE之外评估fn_B()调用,但行为完全一样。
另外其他的变化,如使用旧的查询(可能多次调用fn_B()具有相同的值)具有相同的行为。如果我删除了条件,那么在零时间内我没有记录。如果我把它放回去,那么在10秒内没有记录。
任何想法的人?
感谢您的时间:-)
PS1:我试图重现使用简单的查询在tempdb中的情况,但我无法做到这一点。它只发生在我的实际表格上。 PS2:此查询在另一个函数中调用,因此将结果放入一个临时表中,然后进一步过滤它们也是不可能的。
我想大多数人已经意识到优化器以奇怪的方式重新排列事物。但是有时候查询是以这样的方式编写的,以便程序员负责发生的事情。如果我做两个SELECT DISCTINCTs然后加入他们,我大致确定会发生什么。无论如何,今天我会尝试一些调用,其中内部查询实际带来数据,或者我用一个虚拟函数替换fn_B(),以查看行为是如何改变的。 –
你会这么想,但有很多例外。优化器并不完美。你刚才读过雨果的博客文章和bug报告吗? http://sqlblog.com/blogs/hugo_kornelis/archive/2012/05/04/the-curious-case-of-the-optimizer-that-doesn-t.aspx我见过几种情况下唯一的方法强制我期望从优化器的行为是将查询分解成单独的查询。正如我上面所建议的那样,这只是一个你可以尝试的想法。 –
额外信息。我使用返回6行的参数运行内部查询。零时间。添加WHERE ==> 30秒。我用这些ID对fn(B)进行了6次显式调用,总共0秒。我把整个事情放在探查器中,这里给出的结果是...... SQL Server开始在SAME 5表上一遍又一遍地扫描表扫描(一次又一次,在探查器日志中约有100.000个条目),然后执行查询。所有这些表都出现在fn_B()中,这在原始示例中永远不会被调用。删除NOLOCK并没有什么不同。所以我开始认为这里有一些令人困惑的SQL服务器。 –