Sql-Server

消除會降低性能的 Key Lookup (Clustered) 運算符

  • December 13, 2018

如何在執行計劃中消除 Key Lookup (Clustered) 運算符?

tblQuotes已經有一個聚集索引(on QuoteID)和 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列排序的,這不會告訴我們列中包含哪些TestDat。另一個選項(也是 SQL Server 選擇的選項)包括在IX_Table1非聚集索引中查找行 where Dat = '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,其中包括 的值OthID列的值(主鍵),它自動出現在每個非聚集索引中。聚集索引。計劃:

在此處輸入圖像描述

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