Sql-Server-2012

為什麼 SQL Server 不使用它推薦的索引?

  • May 24, 2020

查詢

我有一個要優化的特定查詢:

SELECT * /*12 columns*/
FROM [dbo].[EnterpriseGroup]
WHERE 
   (EnterpriseGroup.ChildId = 123 OR EnterpriseGroup.FatherId = 234)
AND StatusCd >= 2

也已經有一個索引FatherId,但沒有ChildId。主鍵在 12 個選定的列中,但這裡的 WHERE 子句中沒有使用這些列。

使用

這是一個簡單的查詢,但它在日常工作中非常非常頻繁地執行。該表也很小,大約 8000 行。

該查詢用於查找企業組。大約有 200 萬個企業條目,因此只有不到 0.5% 的企業條目有匹配的組行,因此大多數時候不會找到任何組。

建議

當使用 SSMS 並檢查“實際執行計劃”時,它給出了這個計劃:

不存在索引和建議的執行計劃

無索引查詢時,表掃描懸停資訊。

顯示的謂詞實際上是 WHERE 子句。

並且,它建議創建一個索引,它基本上是在 WHERE 子句上建立索引,並將所有查詢的列直接添加到索引中。對我來說似乎不是很聰明,但也許這就是這個問題的全部內容:

/*
Missing Index Details from SQLQuery6.sql .....
The Query Processor estimates that implementing the following index could improve the query cost by 68.9052%.
*/

/*
USE [...]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[EnterpriseGroup] ([StatusCd])
INCLUDE (....all the 12 queried columns......)
GO
*/

結果

創建推薦索引後,我得到以下計劃:

現有索引的執行計劃

根本沒用!(並且 Table Scan 懸停資訊完全相同,仍然有 WHERE 子句的副本作為謂詞)

問題

為什麼 SQL Server 不使用 SSMS 建議創建的現有索引?

如果不是,那麼在 SSMS(SQL Server Management Studio)針對 Microsoft SQL Server 的執行計劃中缺少索引建議的意義何在?

筆記

注意:我不是 DBA,而是軟體開發人員。我對此進行了一些閱讀,包括:https ://www.brentozar.com/archive/2013/07/dude-who-stole-my-missing-index-recommendation/但我沒有向我澄清。

注意:如果重要: - SQL Server 版本 11.0.7493.4,在 Windows NT 6.3 上執行。- Microsoft SQL Server Management Studio 版本為 11.0.7493.4

假設您的WHERE條件仍然使用ChildId,FatherIdStatusCd。從統計的角度來看,這可能是ChildId或者FatherId更具選擇性。取出ChildIdFatherId取出WHERE子句應該會導致使用該新索引StatusCd,因為indexed column.index

如果您將滑鼠懸停在 Table Scan 部分上,您應該會看到如下圖所示的內容:執行計劃步驟詳情

即使它建議創建該索引,它也有可能仍在通過ChildId或進行查詢FatherId

ChildId如果或FatherId更具選擇性,它會這樣做。假設StatusCd >= 2返回 8,000 行中的 6,000 行。但要麼ChildId = 123FatherId = 234只有 1 行。然後對該列進行表掃描,並在事後應用其餘條件是一個更有效的查詢計劃(從理論上講),而不是返回所有 6,000 行StatusCd >= 2並嘗試應用ChildIdorFatherId條件。

這是我從不久前提出的一個問題中學到的。回答它的人有一個很好的方式來解釋我在這裡想說的話。 查詢優化器是否更喜歡在列之前查詢常量?

希望這會有所幫助。

有許多未知變數需要我們正確回答,例如表大小、列的統計資訊和現有索引。

首先,您的表是一個堆(而不是聚集索引),如果它經常更新,它可能會被轉髮指針碎片化。這可能會對引擎必須執行的讀取次數產生負面影響。

其次,在實際的查詢計劃中,是否可以將滑鼠懸停在表掃描運算符上?它將向我們展示謂詞 - 可能正在進行簡單或強制的參數化,它不會使用統計數據來衡量您的價值。

三、指標是什麼?您需要選擇所有列(選擇星號)嗎?你有關於childId或fatherId的索引嗎?

我推薦的索引(不知道統計資訊將是 1. childId,StatusCd 包括(您的 select 子句中的所有列) 2. FatherId,StatusCd 包括(您的 select 子句中的所有列)

然後我會重寫查詢如下:

SELECT [columns] 
FROM [dbo].[EnterpriseGroup]
WHERE 
   EnterpriseGroup.ChildId = 123 
   AND StatusCd >= 2
UNION ALL
SELECT [columns] 
FROM [dbo].[EnterpriseGroup]
WHERE 
   EnterpriseGroup.FatherId = 234
   AND StatusCd >= 2

您將在同一個表中搜尋兩次,一次使用 Child 上的索引,第二次使用 Father 上的索引,然後連接結果。

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