确定索引是否冗余

Die*_*hon 2 sql-server index-tuning

我有一个具有以下结构的表:

Id (int identity PK)
FooId (int FK)
BarId (int FK)
QuxId (int FK)
Other fields
Run Code Online (Sandbox Code Playgroud)

定义了以下索引:

PK (Id), clustered
Index1 (FooId)
Index2 (FooId, BarId)
Index3 (FooId, BarId, Id, QuxId)
Run Code Online (Sandbox Code Playgroud)

同时保留Index1和是多余的Index2吗?

考虑Id到 PK,将其作为Index3, before 中的第三列是否有意义QuxId

只有一个索引会更有意义(FooId, BarId, QuxId)吗?包括在内会有什么好处Id吗?

(作为旁注,我是开发人员,而不是 DBA)

Tho*_*ger 8

考虑到 Id 是 PK,将它作为 Index3 中的第三列,在 QuxId 之前是否有意义?

这取决于,其背后的原因是因为非聚集索引叶页包含聚集索引键 ( Id)。看看下面的例子:

use TestDB;
go

create table FooBar
(
    Id int identity(1, 1) not null
        constraint PK_FooBar_Id primary key clustered,
    FooId int not null,
    BarId int not null,
    QuxId int not null
);
go

create index Index1
on FooBar(FooId);
go

create index Index2
on FooBar(FooId, BarId);
go

create index Index3
on FooBar(FooId, BarId, QuxId);
go

insert into FooBar(FooId, BarId, QuxId)
values
    (1, 2, 3),
    (2, 3, 4),
    (4, 5, 6),
    (5, 6, 7),
    (6, 7, 8),
    (7, 8, 9);
go


select
    Id,
    FooId,
    BarId,
    QuxId
from FooBar
where FooId = 4;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

通过捕获最后一个查询 (the SELECT)的执行计划,您可以看到它是对 的索引查找[FooBar].[Index3],并注意 myCREATE INDEX 包括该Id列。

马丁在他的评论中提出了一个很好的观点(见下文引用):

但是,如果要求是按该列顺序进行 ORDER,则在索引中的特定位置指定 Id 而不是仅接受默认值可以避免排序。还允许对 FooId、BarId、Id 进行直接查找,而 FooId、BarId、QuxId、Id 上的索引不会这样做(无论 Id 是否包含在 NCI 键或叶中,取决于索引是否声明为唯一的)

这就是他所说的。使用这样的查询(强制索引表提示显示排序操作):

select
    Id,
    FooId,
    BarId,
    QuxId
from FooBar with (index(Index3))
where FooId = 4
order by Id;
Run Code Online (Sandbox Code Playgroud)

这将生成以下计划:

在此处输入图片说明

但是通过改变索引的结构,像这样:

create index Index3
on FooBar(FooId, Id, BarId, QuxId)
with (drop_existing = on);
go
Run Code Online (Sandbox Code Playgroud)

并重新执行上述查询:

select
    Id,
    FooId,
    BarId,
    QuxId
from FooBar -- with (index(Index3))
where FooId = 4
order by Id;
Run Code Online (Sandbox Code Playgroud)

您现在没有昂贵的 Sort 操作:

在此处输入图片说明

现在看看如果您的查询实际上如下所示会发生什么(同样,表提示只是为了 force Index3):

select
    Id,
    FooId,
    BarId,
    QuxId
from FooBar with(index(Index3))
where FooId = 4
and BarId = 5
order by Id;
Run Code Online (Sandbox Code Playgroud)

您将再次进行排序操作。现在,如果您的索引如下所示:

create index Index3
on FooBar(FooId, BarId, Id, QuxId)
with(drop_existing = on);
go
Run Code Online (Sandbox Code Playgroud)

通过Id作为索引结构中的第三个键列,您将看到Id在 Seek Predicates onFooId和时已经对其进行排序的好处BarId。这是更改索引的证明:

在此处输入图片说明

为了直观地了解这一点,让我们看看Index3. 您可以使用以下方法执行此操作:

dbcc ind('TestDB', 'FooBar', -1);
go
-- Index3 (index_id = 4) leaf page PID = 288
dbcc traceon(3604);
go
dbcc page('TestDB', 1, 288, 3);
go
Run Code Online (Sandbox Code Playgroud)

您将获得类似的输出,如下所示:

在此处输入图片说明