AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 183006
Accepted
Serdia
Serdia
Asked: 2017-08-09 15:03:47 +0800 CST2017-08-09 15:03:47 +0800 CST 2017-08-09 15:03:47 +0800 CST

消除会降低性能的 Key Lookup (Clustered) 运算符

  • 772

如何在执行计划中消除 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

执行计划:

在此处输入图像描述

sql-server sql-server-2012
  • 3 3 个回答
  • 43433 Views

3 个回答

  • Voted
  1. Best Answer
    Hannah Vernon
    2017-08-11T12:22:31+08:002017-08-11T12:22:31+08:00

    当查询处理器需要从未存储在用于定位查询返回结果所需的行的索引中的列中获取值时,会发生各种类型的键查找。

    以下面的代码为例,我们正在创建一个带有单个索引的表:

    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非聚集索引中查找行 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,其中包括 的值Oth和ID列的值(主键),它自动出现在每个非聚集索引中。聚集索引。计划:

    在此处输入图像描述

    • 41
  2. Daniel Björk
    2017-08-09T23:36:36+08:002017-08-09T23:36:36+08:00

    导致键查找是因为引擎选择使用不包含您尝试获取的所有列的索引。所以索引没有覆盖 select 和 where 语句中的列。

    要消除键查找,您需要包含缺少的列(键查找的输出列表中的列)= ProducerContactGuid、QuoteStatusID、PolicyTypeID 和 ProducerLocationID,或者另一种方法是强制查询使用聚集索引。

    请注意,表上的 27 个非聚集索引可能对性能不利。运行更新、插入或删除时,SQL Server 必须更新所有索引。这种额外的工作可能会对性能产生负面影响。

    • 7
  3. KumarHarsh
    2017-08-09T21:27:07+08:002017-08-09T21:27:07+08:00

    您忘记提及此查询中涉及的数据量。另外,您为什么要插入临时表?如果只需要显示,则不要运行插入语句。

    出于此查询的目的,tblQuotes不需要 27 个非聚集索引。它需要 1 个聚集索引和 5 个非聚集索引,或者可能需要 6 个非聚集索引。

    此查询需要这些列上的索引:

    QuoteStatusID
    ProducerLocationID
    ProducerContactGuid
    EffectiveDate
    LineGUID
    OriginalQuoteGUID
    

    我还注意到以下代码:

    DATEDIFF(D, @EffDateFrom, tblQuotes.EffectiveDate) >= 0 AND 
    DATEDIFF(D, @EffDateTo, tblQuotes.EffectiveDate) <= 0
    

    即NON Sargable它不能利用索引。

    要使该代码SARgable更改为:

    tblQuotes.EffectiveDate >= @EffDateFrom 
    AND  tblQuotes.EffectiveDate <= @EffDateFrom
    

    要回答您的主要问题,“为什么要查找密钥”:

    您得到KEY Look up的原因是查询中提到的某些列不存在于覆盖索引中。

    您可以 google 并研究Covering Index或Include index。

    在我的示例中,假设 tblQuotes.QuoteStatusID 是非聚集索引,那么我也可以涵盖 DisplayStatus。因为你想要结果集中的 DisplayStatus。任何不存在于索引中但存在于结果集中的列都可以被覆盖以避免KEY Look Up or Bookmark lookup。这是一个覆盖索引的示例:

    create nonclustered index tblQuotes_QuoteStatusID 
    on tblQuotes(QuoteStatusID)
    include(DisplayStatus);
    

    **免责声明:**请记住,上面只是我的示例 DisplayStatus 可能在分析后被其他非 CI 覆盖。

    同样,您必须在查询中涉及的其他表上创建索引和覆盖索引。

    你Index SCAN也在你的计划中。

    这可能是因为表上没有索引,或者当有大量数据时,优化器可能决定扫描而不是执行索引查找。

    这也可能由于High cardinality. 由于连接错误,获得的行数超过了要求。这也可以纠正。

    • 4

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve