TOP 如何(以及為什麼)影響執行計劃?
對於我正在嘗試優化的中等複雜查詢,我注意到刪除
TOP n
子句會更改執行計劃。我會猜到,當查詢包含TOP n
數據庫引擎時會忽略該TOP
子句執行查詢,然後最後將該結果集縮小到請求的n行數。圖形執行計劃似乎表明情況就是這樣——TOP
是“最後”一步。但似乎還有更多的事情發生。我的問題是,TOP n 子句如何(以及為什麼)影響查詢的執行計劃?
這是我的情況的簡化版本:
該查詢匹配兩個表 A 和 B 中的行。
如果沒有該
TOP
子句,優化器估計表 A 中將有 19k 行,表 B 中將有 46k 行。返回的實際行數是 A 的 16k 和 B 的 13k。雜湊匹配用於連接這兩個結果集以獲得總共 69 行(然後應用排序)。這個查詢發生得很快。當我添加
TOP 1001
優化器時不使用雜湊匹配;相反,它首先對錶 A 中的結果進行排序(相同的估計/實際為 19k/16k)並針對錶 B 執行嵌套循環。表 B 的估計行數現在為 1,奇怪的是TOP n
直接影響針對 B 的估計執行次數(索引搜尋)——它似乎總是2n+1,或者在我的情況下是 2003 。如果我改變,這個估計會相應地改變TOP n
。當然,由於這是一個嵌套連接,實際執行次數為 16k(表 A 中的行數),這會減慢查詢速度。實際情況要復雜一些,但這抓住了基本的想法/行為。兩個表都使用索引搜尋進行搜尋。這是 SQL Server 2008 R2 企業版。
我會猜到,當查詢包含 TOP n 時,數據庫引擎會執行查詢而忽略 TOP 子句,然後最後將該結果集縮小到請求的 n 行數。圖形執行計劃似乎表明情況就是這樣——TOP 是“最後”一步。但似乎還有更多的事情發生。
上述措辭的方式讓我認為您可能對查詢的執行方式有一個不正確的心理畫面。查詢計劃中的運算符不是一個步驟(前一步的完整結果集由下一個步驟評估。
SQL Server 使用流水線執行模型,其中每個運算符都公開Init()、*GetRow()和Close()等方法。正如GetRow()*名稱所暗示的那樣,操作員根據需要一次生成一行(根據其父操作員的要求)。這在 Books Online Logical and Physical Operators 參考中有記錄,在我的部落格文章Why Query Plans Run Backwards中有更多詳細資訊。這種一次一行的模型對於為查詢執行形成合理的直覺至關重要。
我的問題是,n 子句如何(以及為什麼)
TOP
影響查詢的執行計劃?一些邏輯操作
TOP
,如半連接和FAST n
查詢提示,會影響查詢優化器對執行計劃備選方案進行成本計算的方式。基本思想是,一個可能的計劃形狀可能比經過優化以返回所有行的不同計劃更快地返回前n行。例如,索引嵌套循環連接通常是返回少量行的最快方法,儘管散列或合併連接與掃描在較大的集合上可能更有效。查詢優化器對這些選擇進行推理的方式是在操作邏輯樹中的特定點設置行目標。
行目標修改查詢計劃備選方案的成本計算方式。它的本質是優化器首先計算每個運算符的成本,就好像需要完整的結果集一樣,在適當的點設置行目標,然後沿著計劃樹向下工作,估計它預計需要檢查的行數以滿足行目標。
例如,邏輯
TOP(10)
在邏輯查詢樹中的特定點設置行目標 10。導致行目標的操作員成本被修改以估計他們需要生產多少行才能滿足行目標。此計算可能會變得複雜,因此通過完整的範例和帶註釋的執行計劃更容易理解所有這些。行目標可能影響的不僅僅是連接類型的選擇或查找和查找是否比掃描更受歡迎。更多細節在這裡。與往常一樣,基於行目標選擇的執行計劃取決於優化器的推理能力和提供給它的資訊質量。並非每個具有行目標的計劃在實踐中都會更快地生成所需的行數,但根據成本計算模型,它會。
在行目標計劃被證明不是更快的情況下,通常有一些方法可以修改查詢或向優化器提供更好的資訊,以便自然選擇的計劃是最好的。哪個選項適合您的情況當然取決於細節。行目標功能通常非常有效(儘管在並行執行計劃中使用時需要注意一個錯誤)。
您的特定查詢和計劃可能不適合在此處進行詳細分析(如果您願意,請務必提供實際的執行計劃),但希望此處概述的想法能讓您取得進展。