为啥 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)