涵蓋索引更改執行計劃但未使用
我有以下偶爾執行緩慢的查詢:
SELECT C.CustomerID FROM dbo.Customers C WITH (NOLOCK) WHERE C.Forename = @Forename AND C.Surname = @Surname OPTION (RECOMPILE)
CustomerID 是客戶表上的主鍵。Customers 表還具有以下兩個非聚集索引:
CREATE NONCLUSTERED INDEX idx_Forename ON Customers (Forename ASC) CREATE NONCLUSTERED INDEX idx_Surname ON Customers (Surname ASC)
當我執行輸入了姓氏和名字的查詢時,查詢優化器使用索引“idx_Surname”,如下面的執行計劃所示:
對於此特定搜尋,此查詢需要兩分鐘多的時間才能完成,但未找到任何結果。對於輸入的值,@Forename 在客戶表中沒有匹配項,而@Surname 匹配 31,162 條記錄。當我只按@surname 搜尋時,31,162 條記錄會在一秒鐘內返回,並採用以下計劃:
為了優化包含 Forename 和 Surname 的搜尋查詢,我添加了以下覆蓋索引:
CREATE NONCLUSTERED INDEX idx_Surname_Covering ON dbo.Customers (Surname) INCLUDE (Forename)
包含 Forename 和 Surname 的查詢將在不到一秒的時間內返回。但是,實際執行計劃中並沒有使用覆蓋索引:
所以,
- 是否需要覆蓋指數或是否有更好的方法來提高性能和
- 為什麼附加覆蓋索引會導致實際執行計劃中的索引從idx_Forename變為idx_Surname?
ps 上面的查詢是一個孤立的範例,在使用時,可以搜尋姓氏或名字或兩者,並且客戶表還包括其他具有自己索引的可搜尋列。這個細節被認為與問題無關,所以我沒有包括它。
1)是否需要覆蓋指數或有更好的方法來提高性能
最佳指數
對於訪問表的查詢,最好的索引是覆蓋面最大、選擇性最強的索引。
以您的表為例,您有 50000 行 firstname = John ,但只有一個 last name = ‘McClane’,您應該使用 John 作為 First 鍵值還是 McClane 創建索引?
回答:
這取決於…如果您總是在搜尋 John Mcclane,那麼它是一個打開和關閉首先索引姓氏的案例。但是,如果還有搜尋康斯坦丁史密斯的查詢呢?你可以擁有超過 5000 個史密斯,但只有五個康斯坦丁。
結果,這取決於您的查詢以及您正在尋找的內容,執行多少,……
如果您的查詢總是要同時搜尋名字和姓氏,那麼選擇更具選擇性的一個作為第一個鍵列是一個簡單的情況。請記住,讀取性能的提高應該大於寫入性能的下降。
當然,沒有人限制您創建兩個索引,一個使用 (firstname,lastname),一個使用 (lastname,firstname)。
(您的更新/插入/刪除語句可能)。
不考慮過濾索引之類的,您範例的最佳索引是:
CREATE NONCLUSTERED INDEX idx_Forename_Surname ON dbo.Customers (Forename,Surname)
2)為什麼附加覆蓋索引會導致實際執行計劃中的索引從idx_Forename變為idx_Surname?
我不認為這僅僅是因為索引,而是因為創建索引時創建的統計資訊。
儘管這些統計資訊與 idx_Surname 中的統計資訊相同,但我的猜測是它們具有更大的採樣率(100),因為它們是使用“fullscan”創建的。
如果在 index 創建的統計資訊上發生了自動更新統計資訊
idx_Surname
,則它們可能具有較小的採樣率,從而導致錯誤估計(例如 1% 採樣率)。您可以嘗試刪除
idx_Surname_Covering
索引及其統計資訊,並dbo.Customers
以 100% 的採樣率(全掃描)更新統計資訊以測試該理論。UPDATE STATISTICS dbo.Customers WITH FULLSCAN
這有望改變您使用更好搜尋的計劃。
如果這就是您的查詢更改的原因,並且在維護視窗中使用全掃描更新統計資訊不是一個可行的選擇,您可以更改採樣率