为啥 SQL Server 在“选择 *”操作中在聚簇 PK 上使用非聚簇索引?

Posted

技术标签:

【中文标题】为啥 SQL Server 在“选择 *”操作中在聚簇 PK 上使用非聚簇索引?【英文标题】:Why does SQL Server use a non-clustered index over the clustered PK in a "select *" operation?为什么 SQL Server 在“选择 *”操作中在聚簇 PK 上使用非聚簇索引? 【发布时间】:2011-10-19 16:42:32 【问题描述】:

我有一个非常简单的表格,用于存储人们的标题(“先生”、“夫人”等)。这是我正在做的事情的简要版本(在这个例子中使用了一个临时表,但结果是一样的):

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 在聚集主键上使用非聚集索引。我猜这解释了为什么结果按此顺序返回,但我不知道为什么会这样。

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

谢谢!

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

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

    没有一个指标优于另一个指标的标准 唯一索引覆盖返回的数据(标题和PK);这在我看来有点推测,但 SQL Server 正在做它认为最好的事情。

在具有未涵盖的附加列的表上尝试它 - 没有赌注,但它可能会让查询计划者改变主意。

编码愉快。

【讨论】:

谢谢@pst - 我尝试了你的建议并添加到另一列(性别位不为空),果然执行计划然后通过聚集索引运行。【参考方案3】:

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

对于这样一个微不足道的表,选择哪个访问路径并不重要,因为更差的路径仍然只有两个 IO。

正如上面有人评论的那样,如果您希望您的数据按特定顺序排列,您必须使用“ORDER BY”子句明确请求此操作,否则您得到的结果非常随机。

【讨论】:

【参考方案4】:

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

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

【讨论】:

谢谢@marc_s - 每个人似乎都说了同样的话 - 非聚集索引更小 - 所以我会将你的答案标记为第一次正确。干杯!

以上是关于为啥 SQL Server 在“选择 *”操作中在聚簇 PK 上使用非聚簇索引?的主要内容,如果未能解决你的问题,请参考以下文章

为啥安装sql Server 2008时总是显示windows powershell错误?

SQL Server 2016 - 为啥我需要为我的选择语句指定一个根元素

为啥SQL数据库不能用SQL Server身份登录 提示用户sa登录失败。(Microsoft SQL server,错误18456)

为啥SQL数据库不能用SQL Server身份登录 提示用户sa登录失败。(Microsoft SQL server,错误18456)

为啥 SQL Server 会改变操作顺序和装箱方式?

更改 SQL Server 2008 中在表的计算列中引用的标量函数