为什么SQL Server在“select *”操作中对群集PK使用非聚集索引?

时间:2011-08-04 06:09:16

标签: sql-server clustered-index sql-execution-plan non-clustered-index

我有一个非常简单的桌子,为人们存储标题(“先生”,“太太”等)。这是我正在做的简短版本(在这个例子中使用临时表,但结果是相同的):

create table #titles (
    t_id    tinyint     not null    identity(1, 1),
    title   varchar(20) not null,

    constraint pk_titles primary key clustered (t_id),
    constraint ux_titles unique nonclustered (title)
)
go

insert #titles values ('Mr')
insert #titles values ('Mrs')
insert #titles values ('Miss')

select * from #titles

drop table #titles

请注意,表的主键是聚类的(显式地,为了示例),并且标题列中存在非聚集唯一性约束。

以下是选择操作的结果:

t_id title
---- --------------------
3    Miss
1    Mr
2    Mrs

查看执行计划, SQL使用群集主键上的非聚集索引。我猜这解释了为什么结果按此顺序返回,但我不知道为什么会这样做。

有什么想法吗?更重要的是,任何阻止这种行为的方法?我希望按照插入的顺序返回行。

谢谢!

4 个答案:

答案 0 :(得分:6)

如果您需要订单,则需要指定明确的ORDER BY - 生成订单的任何其他内容(它的“订单”是随机的并且可能会更改)。 SQL Server中没有隐含的顺序 - 不是任何东西。如果您需要订单 - 请ORDER BY说明。

SQL Server可能使用非聚集索引(如果它可以 - 如果该索引具有您的查询要求的所有列),因为它更小 - 通常只是索引列和聚类键(再次:一列或多列)。另一方面,聚簇索引是整个数据(在叶级别),因此可能需要读取更多数据,以便得到答案(当然不是在这个过于简化的示例中 - 而是在现实世界)。

答案 1 :(得分:5)

唯一的方式(绝对和正确)保证行顺序是使用ORDER BY - 其他任何东西都是一个实现细节,容易爆炸,如图所示。

至于为什么引擎选择了唯一索引:它没关系。

  1. 没有标准支持一个索引而不是另一个索引
  2. 唯一索引涵盖返回的数据(标题和PK);这对我来说有些推测,但SQL Server正在做它认为最好的事情。
  3. 在一个没有覆盖的附加列的表上尝试它 - 没有赌注,但它可能会使查询计划者改变主意。

    快乐的编码。

答案 2 :(得分:4)

SQLServer 可能选择了非聚集索引,因为您所请求的所有数据(id和title)都可以从该索引中检索。

对于这样一个简单的表,选择哪个访问路径并不重要,因为更糟糕的路径仍然只有两个IO。

如上所述,如果您希望按特定顺序使用数据,则必须使用“ORDER BY”子句特定地请求此项,否则您将获得相应的随机性。

答案 3 :(得分:3)

非聚簇索引通常小于聚簇索引,因此扫描非聚簇索引而不是聚簇索引通常会更快。这可能解释了SQL Server对非聚簇索引的偏好,即使在您的情况下索引的大小相同。

保证返回行顺序的唯一方法是指定ORDER BY。如果您没有指定ORDER BY,那么您隐式告诉优化器它可以选择返回行的顺序。

相关问题