Sql-Server

涵蓋索引更改執行計劃但未使用

  • January 30, 2019

我有以下偶爾執行緩慢的查詢:

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 的查詢將在不到一秒的時間內返回。但是,實際執行計劃中並沒有使用覆蓋索引:

覆蓋指數的執行計劃

所以,

  1. 是否需要覆蓋指數或是否有更好的方法來提高性能和
  2. 為什麼附加覆蓋索引會導致實際執行計劃中的索引從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

這有望改變您使用更好搜尋的計劃。

如果這就是您的查詢更改的原因,並且在維護視窗中使用全掃描更新統計資訊不是一個可行的選擇,您可以更改採樣率

引用自:https://dba.stackexchange.com/questions/228442