消除會降低性能的 Key Lookup (Clustered) 運算符
如何在執行計劃中消除 Key Lookup (Clustered) 運算符?
表
tblQuotes
已經有一個聚集索引(onQuoteID
)和 27 個非聚集索引,所以我試圖不再創建。我把聚集索引列
QuoteID
放在我的查詢中,希望它會有所幫助——但不幸的是還是一樣。或查看:
這就是 Key Lookup 運算符所說的:
詢問:
declare @EffDateFrom datetime ='2017-02-01', @EffDateTo datetime ='2017-08-28' SET NOCOUNT ON SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED IF OBJECT_ID('tempdb..#Data') IS NOT NULL DROP TABLE #Data CREATE TABLE #Data ( QuoteID int NOT NULL, --clustered index [EffectiveDate] [datetime] NULL, --not indexed [Submitted] [int] NULL, [Quoted] [int] NULL, [Bound] [int] NULL, [Exonerated] [int] NULL, [ProducerLocationId] [int] NULL, [ProducerName] [varchar](300) NULL, [BusinessType] [varchar](50) NULL, [DisplayStatus] [varchar](50) NULL, [Agent] [varchar] (50) NULL, [ProducerContactGuid] uniqueidentifier NULL ) INSERT INTO #Data SELECT tblQuotes.QuoteID, tblQuotes.EffectiveDate, CASE WHEN lstQuoteStatus.QuoteStatusID >= 1 THEN 1 ELSE 0 END AS Submitted, CASE WHEN lstQuoteStatus.QuoteStatusID = 2 or lstQuoteStatus.QuoteStatusID = 3 or lstQuoteStatus.QuoteStatusID = 202 THEN 1 ELSE 0 END AS Quoted, CASE WHEN lstQuoteStatus.Bound = 1 THEN 1 ELSE 0 END AS Bound, CASE WHEN lstQuoteStatus.QuoteStatusID = 3 THEN 1 ELSE 0 END AS Exonareted, tblQuotes.ProducerLocationID, P.Name + ' / '+ P.City as [ProducerName], CASE WHEN tblQuotes.PolicyTypeID = 1 THEN 'New Business' WHEN tblQuotes.PolicyTypeID = 3 THEN 'Rewrite' END AS BusinessType, tblQuotes.DisplayStatus, tblProducerContacts.FName +' '+ tblProducerContacts.LName as Agent, tblProducerContacts.ProducerContactGUID FROM tblQuotes INNER JOIN lstQuoteStatus on tblQuotes.QuoteStatusID=lstQuoteStatus.QuoteStatusID INNER JOIN tblProducerLocations P On P.ProducerLocationID=tblQuotes.ProducerLocationID INNER JOIN tblProducerContacts ON dbo.tblQuotes.ProducerContactGuid = tblProducerContacts.ProducerContactGUID WHERE DATEDIFF(D,@EffDateFrom,tblQuotes.EffectiveDate)>=0 AND DATEDIFF(D, @EffDateTo, tblQuotes.EffectiveDate) <=0 AND dbo.tblQuotes.LineGUID = '6E00868B-FFC3-4CA0-876F-CC258F1ED22D'--Surety AND tblQuotes.OriginalQuoteGUID is null select * from #Data
執行計劃:
當查詢處理器需要從未儲存在用於定位查詢返回結果所需的行的索引中的列中獲取值時,會發生各種類型的鍵查找。
以下面的程式碼為例,我們正在創建一個帶有單個索引的表:
USE tempdb; IF OBJECT_ID(N'dbo.Table1', N'U') IS NOT NULL DROP TABLE dbo.Table1 GO CREATE TABLE dbo.Table1 ( Table1ID int NOT NULL IDENTITY(1,1) , Table1Data nvarchar(30) NOT NULL ); CREATE INDEX IX_Table1 ON dbo.Table1 (Table1ID); GO
我們將在表中插入 1,000,000 行,以便我們可以處理一些數據:
INSERT INTO dbo.Table1 (Table1Data) SELECT TOP(1000000) LEFT(c.name, 30) FROM sys.columns c CROSS JOIN sys.columns c1 CROSS JOIN sys.columns c2; GO
現在,我們將使用選項查詢數據以顯示“實際”執行計劃:
SELECT * FROM dbo.Table1 WHERE Table1ID = 500000;
查詢計劃顯示:
查詢查看
IX_Table1
索引以查找行,Table1ID = 5000000
因為查看該索引比掃描整個表以查找該值要快得多。但是,為了滿足查詢結果,查詢處理器還必須找到表中其他列的值;Table1ID
這就是“RID 查找”的用武之地。它在表中查找與包含500000 值的行關聯的行 ID(RID 查找中的 RID),並從Table1Data
列中獲取值。如果將滑鼠懸停在計劃中的“RID Lookup”節點上,您會看到:“輸出列表”包含 RID 查找返回的列。
具有聚集索引和非聚集索引的表是一個有趣的例子。下表共有三列;ID 是聚集鍵,
Dat
由非聚集索引IX_Table
和第三列索引Oth
。USE tempdb; IF OBJECT_ID(N'dbo.Table1', N'U') IS NOT NULL DROP TABLE dbo.Table1 GO CREATE TABLE dbo.Table1 ( ID int NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED , Dat nvarchar(30) NOT NULL , Oth nvarchar(3) NOT NULL ); CREATE INDEX IX_Table1 ON dbo.Table1 (Dat); GO INSERT INTO dbo.Table1 (Dat, Oth) SELECT TOP(1000000) CRYPT_GEN_RANDOM(30), CRYPT_GEN_RANDOM(3) FROM sys.columns c CROSS JOIN sys.columns c1 CROSS JOIN sys.columns c2; GO
以這個範例查詢為例:
SELECT * FROM dbo.Table1 WHERE Dat = 'Test';
我們要求 SQL Server 從表中返回
Dat
包含單詞的每一列Test
。我們在這裡有幾個選擇;我們可以查看表(即聚集索引)——但這需要掃描整個事物,因為表是按ID
列排序的,這不會告訴我們列中包含哪些Test
行Dat
。另一個選項(也是 SQL Server 選擇的選項)包括在IX_Table1
非聚集索引中查找行 whereDat = 'Test'
,但是由於我們也需要該Oth
列,SQL Server 必須使用“鍵”對聚集索引執行查找查找”操作。這是為此的計劃:如果我們修改非聚集索引使其包含該
Oth
列:DROP INDEX IX_Table1 ON dbo.Table1; GO CREATE INDEX IX_Table1 ON dbo.Table1 (Dat) INCLUDE (Oth); <---- This is the only change GO
然後重新執行查詢:
SELECT * FROM dbo.Table1 WHERE Dat = 'Test';
我們現在看到一個非聚集索引查找,因為 SQL Server 只需要定位索引
Dat = 'Test'
中的行IX_Table1
,其中包括 的值Oth
和ID
列的值(主鍵),它自動出現在每個非聚集索引中。聚集索引。計劃: