Fle*_*tch 9 index sql-server execution-plan sql-server-2016
我正在调试运行缓慢的查询,并且在执行计划中建议使用非聚集索引,影响为 51.6648。但是,非聚集索引仅包括主键 (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 ...)
Run Code Online (Sandbox Code Playgroud)
该表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
)
Run Code Online (Sandbox Code Playgroud)
Ran*_*gen 12
表 Retailer_Relations 具有以下复合 PK 索引和建议索引 -
虽然缺少索引可能会有所帮助并且肯定可以工作,但我不会在缺少索引上花费太多时间,这些提示是在估计的执行计划上创建的,而不是在实际执行计划上。
更准确地说,这些索引提示是以降低计划中运营商使用的Query Bucks™成本为前提的。优化器计算估计成本,并相应地添加缺失的索引提示。
结果他们可能是非常错误的。如果您不确定它是否会有所帮助,最好的做法是在前后测试情况。您可以通过SET STATISTICS IO, TIME ON;
在运行查询之前添加语句来完成此
操作。
此外,您可以使用statisticsparser来更轻松地阅读这些统计信息。
这可能是因为索引中列的顺序吗?
没错,创建缺失索引可以提高查询的选择性,例如,如果您的查询如下所示:
SELECT RelatedRetailerID
FROM Retailer_Relations
WHERE
RetailerID = 5 AND
RelationType = 20;
Run Code Online (Sandbox Code Playgroud)
或者像这样:
SELECT RelatedRetailerID
FROM Retailer_Relations
ORDER BY
RetailerID,
RelationType;
Run Code Online (Sandbox Code Playgroud)
这背后的原因是两个索引都可以在 RetailerID 上查找,这部分不会改变。但是,如果在 RelationType 上应用了额外的过滤器/排序呢?它会在聚集索引中无处不在,因为它是第三个键值,而不是第二个键值。正如我们所知,它是 NCI 中的第二个关键值。
好的,但是非聚集索引何时或如何改进查询?
几种情况可能是:
NCI 旁注
作为旁注,并不完全需要将键列添加到 NCI 中的包含列表中,因为 CI 键列会自动包含在所有非聚集索引中。
如果您不确定聚集索引是否会保持不变,并且希望始终包含该列,则可以选择这样做。
关于查询本身,如果您通过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
Run Code Online (Sandbox Code Playgroud)
查询#1
SELECT RelatedRetailerID
FROM Retailer_Relations
WHERE
RetailerID = 5 AND
RelationType = 20;
Run Code Online (Sandbox Code Playgroud)
没有索引的计划在这里
当它进行搜索时,它正在对 RetailerID 进行搜索。之后它在 RelationType 上发出一个残留的 I/O 谓词
添加索引
CREATE NONCLUSTERED INDEX IX_TEST
ON Retailer_Relations (
RetailerID,
RelationType
)
INCLUDE (
RelatedRetailerID
)
Run Code Online (Sandbox Code Playgroud)
剩余谓词消失了,一切都发生在两列上的搜索谓词中。
通过第二个查询,增加的索引有用性变得更加明显:
SELECT RelatedRetailerID
FROM Retailer_Relations
ORDER BY
RetailerID,
RelationType;
Run Code Online (Sandbox Code Playgroud)
没有索引的计划,使用 Sort 运算符:
用索引规划,用索引去掉排序操作符