缺少的非聚集索引已成为聚集索引的一部分


9

我正在调试运行缓慢的查询,在执行计划中建议使用51.6648 Impact的非聚集索引。但是,非聚集索引仅包括主键(PK)复合聚集索引中已经存在的列。

难道是因为索引中列的顺序?即,如果聚集索引中的列从最有选择性到最少的顺序不顺序,那么非聚集索引是否有可能提高性能?

此外,非聚集索引仅包含三个PK列中的两个,而第三个添加为包含列。include使用非聚集索引可能会更优化的另一个原因吗?

以下是我正在使用的表结构的示例:

桌子-

Retailers (
    RetailerID int PK, 
    name ...)

Retailer_Relation_Types (
    RelationType smallint PK, 
    Description nvarchar(50) ...)

Retailer_Relations (
    RetailerID int PK FK, 
    RelatedRetailerID int PK FK, 
    RelationType smallint PK FK, 
    CreatedOn datetime ...)

该表Retailer_Relations具有以下综合PK指数和建议指数-

CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY]

CREATE NONCLUSTERED INDEX <NameOfIndex> 
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )

Answers:


12

表Retailer_Relations具有以下复合PK索引和建议的索引-

尽管缺少索引可能会有所帮助并且一定可以工作,但我不会在丢失索引上花费太多时间,这些提示是在估计的执行计划上创建的,而不是在实际的执行计划上创建的。

更准确地说,这些索引提示是基于降低计划中的运营商使用Query Bucks™的成本的前提。优化器计算估计的成本,并相应地添加缺失的索引提示。

结果,它们可能是非常错误的。如果不确定是否会有所帮助,最好的办法是前后测试情况。您可以通过SET STATISTICS IO, TIME ON;在运行查询之前添加语句来做到这一点 。

另外,您可以使用statisticsparser使其更易于阅读这些统计信息。

难道是因为索引中列的顺序?

没错,创建丢失的索引可以提高查询的选择性,例如,查询如下所示:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

或像这样:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

这背后的原因是两个索引都可以在RetailerID上查找,该部分不会改变。但是,如果在RelationType上应用了额外的过滤器/排序,该怎么办?由于它是第三个键值,而不是第二个键值,因此它将在聚集索引中的所有位置。众所周知,这是NCI中的第二个关键值。

好的,但是非聚集索引何时或如何改善查询?

几种情况可能是:

  • 如果RelationType过滤了大量值,则剩余I / O可能会很高,从而可能需要非聚集索引(查询#1)
  • 在两列上发生排序(一种方式),并且结果集很大(查询2)。
  • 正如@AaronBertrand所提到的:如果CI大小与NCI相比有很大差异,则添加NCI将减少受益于它的查询所读取的页面。

NCI旁注

附带说明一下,由于CI关键字列自动包含在所有非聚簇索引中,因此并不完全需要将关键字列添加到NCI的包含列表中。

如果您不确定聚集索引是否保持不变,并且希望始终包含该列,则可以选择这样做。

关于查询本身,如果您通过PasteThePlan添加了执行计划, 我们可以提供更多有关索引/改进查询的信息。


测试中

创建表并添加一些行

CREATE TABLE Retailer_Relations (
    RetailerID int , 
    RelatedRetailerID int , 
    RelationType smallint, 
    CreatedOn datetime,
    CONSTRAINT PK_Retailer_Relations 
PRIMARY KEY CLUSTERED (
    RetailerID ASC, 
    RelatedRetailerID ASC, 
    RelationType ASC
    ) ON [PRIMARY])


    DECLARE @I Int = 1
    WHILE @I < 1000
    BEGIN
    INSERT INTO Retailer_Relations(RetailerID,RelatedRetailerID,RelationType,CreatedOn)
    VALUES(@I,@I,@I,GETDATE()
    )
    set @I += 1
    END

查询#1

    SELECT  RelatedRetailerID
FROM Retailer_Relations 
WHERE
RetailerID = 5 AND
RelationType = 20;

没有索引的计划在这里

在搜索时,它正在对RetailerID进行搜索。之后,它会在RelationType上发布剩余I / O谓词

添加索引

CREATE NONCLUSTERED INDEX IX_TEST
ON Retailer_Relations (
    RetailerID, 
    RelationType
    ) 
INCLUDE (
    RelatedRetailerID
    )

剩余谓词都消失了,所有情况都在两列的搜索谓词中发生。

执行计划

通过第二个查询,添加的索引有用性变得更加明显:

SELECT  RelatedRetailerID
FROM Retailer_Relations 
ORDER BY
RetailerID,
RelationType;

使用排序运算符在没有索引的情况下进行计划:

在此处输入图片说明

使用索引计划,使用索引删除排序运算符

在此处输入图片说明


1
谢谢兰迪,我将其标记为答案,但我只是想问一下,您是说失踪指数建议是基于估计执行计划吗?我问这个问题,因为它显示在SS2016的实际执行计划中。
弗莱奇

1
我想知道这是否是您的意思,谢谢您的澄清。
Fletch
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.