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)
考虑到 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)
您将获得类似的输出,如下所示: