2012-05-03 165 views
1

今天我參加了太多的會議,但我認爲我還有自己的智能軟件。 在我的努力來改善某些查詢我碰到下面的神祕來到的表現(表名和字段轉述):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:此查詢在另一個函數中調用,因此將結果放入一個臨時表中,然後進一步過濾它們也是不可能的。

回答

0

就像一個說明,優化器不會按照您的方式讀取查詢。即使您認爲某個順序應該發生,或者短路可能最有意義,優化程序仍然可以按照您預期的順序評估CTE /子查詢。您可能會嘗試的解決方法是將第一個查詢選擇到#temp表中,然後在#temp表上運行函數過濾器。這應該強制評估的順序,即使它是完全不直觀的,也不那麼優雅。

編輯

此外,雖然它可以執行慢,我很好奇,如果你沒有NOLOCK運行查詢,或RCSI而不是發生了什麼。不同的鎖定語義可能會跳過優化器。

+0

我想大多數人已經意識到優化器以奇怪的方式重新排列事物。但是有時候查詢是以這樣的方式編寫的,以便程序員負責發生的事情。如果我做兩個SELECT DISCTINCTs然後加入他們,我大致確定會發生什麼。無論如何,今天我會嘗試一些調用,其中內部查詢實際帶來數據,或者我用一個虛擬函數替換fn_B(),以查看行爲是如何改變的。 –

+0

你會這麼想,但有很多例外。優化器並不完美。你剛纔讀過雨果的博客文章和bug報告嗎? http://sqlblog.com/blogs/hugo_kornelis/archive/2012/05/04/the-curious-case-of-the-optimizer-that-doesn-t.aspx我見過幾種情況下唯一的方法強制我期望從優化器的行爲是將查詢分解成單獨的查詢。正如我上面所建議的那樣,這只是一個你可以嘗試的想法。 –

+0

額外信息。我使用返回6行的參數運行內部查詢。零時間。添加WHERE ==> 30秒。我用這些ID對fn(B)進行了6次顯式調用,總共0秒。我把整個事情放在探查器中,這裏給出的結果是...... SQL Server開始在SAME 5表上一遍又一遍地掃描表掃描(一次又一次,在探查器日誌中約有100.000個條目),然後執行查詢。所有這些表都出現在fn_B()中,這在原始示例中永遠不會被調用。刪除NOLOCK並沒有什麼不同。所以我開始認爲這裏有一些令人困惑的SQL服務器。 –

0

我們將問題提交給Microsoft支持SQL Server R2(我必須評論他們驚人的響應時間和整體服務程序)。我們給了他們我們的數據庫的副本再現問題,我們的解決辦法,他們複製它自己,後一對夫婦在這裏的天是我們回來了答案:

我分析這兩個執行計劃,並會親切詢問 解決方法是否可以接受用於生產?它後面的主要原因是 ,是一個函數沒有的,因爲索引有, 統計。而這種數據的缺乏使得優化器有時會選擇一個不太好的執行計劃。如果您已經找到了解決方法,那麼最好實施這個方案是 。我們嘗試的索引更改沒有改進執行。

這是相當外交的方式來說「是的,優化程序與您的查詢混淆了事情,所以請使用解決方法」。如果你想把它稱爲一個bug,把它稱爲一個bug,這沒關係。

只是爲了記錄,解決方法是將調用fn_B()放入SELECT DISTINCT上一級查詢的SELECT列表中,然後在WHERE條件中篩選其結果。有點奇怪,但它有訣竅。