索引键列顺序

dav*_*vey 3 index sql-server-2008 sql-server primary-key

我为多对多关系创建了一个连接表。

该表中只有 2 个列,ticketid并且groupid

典型的数据是

groupid    ticketid
20         56  
20         87
20         96
24         13
24         87
25         5
Run Code Online (Sandbox Code Playgroud)

我的问题是在创建复合键时我应该ticketid遵循groupid

CONSTRAINT [PK_ticketgroup] PRIMARY KEY CLUSTERED 
    (
        [ticketid] ASC,
        [groupid] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

或者groupid反过来ticketid

CONSTRAINT [PK_ticketgroup] PRIMARY KEY CLUSTERED 
        (
            [groupid] ASC,
                    [ticketid] ASC
        )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
        ) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

在选项 1 中搜索索引是否会更快,因为它ticketid有更多的机会成为唯一的,然后groupid它们将位于组合键的开头?或者这是微不足道的?

Tho*_*ger 8

这一切都取决于您从该聚集索引中检索数据的准确程度。聚集索引将按前导键列排序,然后按索引定义中定义的每个后续键列排序。看到这一点的最好方法是通过一个例子。


测试对象设置

use TestDB;
go

if exists (select 1 from sys.tables where name = 'TestTable2')
begin
    drop table TestTable2;
end
create table TestTable2
(
    groupid int not null,
    ticketid int not null
);
go

insert into TestTable2(groupid, ticketid)
values
    (20, 56),  
    (20, 87),
    (20, 96),
    (24, 13),
    (24, 87),
    (25, 5 );
go
Run Code Online (Sandbox Code Playgroud)

测试1(groupid,ticketid)Key Column Order

现在让我们创建在键列上定义的聚集索引(groupid, ticketid)

alter table TestTable2
add constraint PK_TestTable2
primary key clustered (groupid, ticketid);
go
Run Code Online (Sandbox Code Playgroud)

现在我们想看看这些数据是如何存储在页面中的:

dbcc ind('TestDB', 'TestTable2', 1);
go
-- FileID:1 PageID:310

if exists (select 1 from tempdb.sys.tables where name like '#DbccPage%')
begin
    drop table #DbccPage;
end
create table #DbccPage
(
    ParentObject varchar(128) null,
    Object varchar(128) null,
    Field varchar(128) null,
    Value varchar(128) null
);
go

insert into #DbccPage
exec ('dbcc page(TestDB, 1, 310, 3) with tableresults;');
go

select
    Object,
    Field,
    Value
from #DbccPage
where ParentObject not in
(
    'page header:',
    'buffer:'
)
and Object not like 'memory dump%';
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

我们看到它实际上是按groupidfirst排序的,然后是ticketid。这将导致以下查询成为索引查找:

-- this is an index seek
select *
from TestTable2
where groupid = 25;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

此查询将是一次扫描,因为它无法搜索ticketid

-- this is an index scan
select *
from TestTable2
where ticketid = 13;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


测试2(ticketid, groupid)Key Column Order

但是现在让我们改变它并交换键列的顺序:

alter table TestTable2
drop constraint PK_TestTable2;
go

alter table TestTable2
add constraint PK_TestTable2
primary key clustered (ticketid, groupid);
go
Run Code Online (Sandbox Code Playgroud)

现在我们来看看数据在索引页上是怎样的:

dbcc ind('TestDB', 'TestTable2', 1);
go
-- FileID:1 PageID:308

if exists (select 1 from tempdb.sys.tables where name like '#DbccPage%')
begin
    drop table #DbccPage;
end
create table #DbccPage
(
    ParentObject varchar(128) null,
    Object varchar(128) null,
    Field varchar(128) null,
    Value varchar(128) null
);
go

insert into #DbccPage
exec ('dbcc page(TestDB, 1, 308, 3) with tableresults;');
go

select
    Object,
    Field,
    Value
from #DbccPage
where ParentObject not in
(
    'page header:',
    'buffer:'
)
and Object not like 'memory dump%';
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

现在我们看到数据是按ticketidfirst排序的,然后是groupid。这将改变我们的测试查询的行为,如下所示:

-- this is an index scan
select *
from TestTable2
where groupid = 25;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

-- this is an index seek
select *
from TestTable2
where ticketid = 13;
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


概括

正如您在上面看到的,它归结为您的工作负载和命中此聚集索引的查询类型。根据键列的顺序及其定义方式,能够根据WHERE子句(或 a JOIN)进行查找或扫描是不同的。