今天我參加了太多的會議,但我認爲我還有自己的智能軟件。 在我的努力來改善某些查詢我碰到下面的神祕來到的表現(表名和字段轉述):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服務器。 –