为什么 SQL Server 使用非聚集索引而不使用聚集索引?

4 index sql-server clustered-index nonclustered-index sql-server-2014

我有一张有 1.45 亿行的表

CREATE TABLE [dbo].[RFTest](
    [SnapshotKey] [int] NOT NULL,
    [SnapshotDt] [datetime] NOT NULL,
    [LoanNum] [int] NOT NULL,
    [GLSourceSystem] [varchar](10) NOT NULL,
    [FlowDescription] [varchar](30) NULL,
    [Account] [varchar](30) NULL,

    --- plus 20 more column
)
Run Code Online (Sandbox Code Playgroud)

该表在 SnapshotDt 上分区。

我在我的表上添加了以下索引:

create clustered index ci on RFTest (SnapshotDt, SnapshotKey, LoanNum)

create nonclustered index nci on RFTest (SnapshotDt, SnapshotKey, LoanNum)
    include ([GLSourceSystem],[Account],[FlowDescription])
Run Code Online (Sandbox Code Playgroud)

我运行了以下查询:(我使用前 100 名进行测试,因为如果我想运行它,整个表将需要很长时间)

select top 100 *
from RFTest with (index(ci)) -- force index
where LoanNum = 2712

select top 100 *
from RFTest 
where LoanNum = 2712
Run Code Online (Sandbox Code Playgroud)

LoanNum列存在于两个索引中,部分键在聚簇中,包括在非聚簇中。

执行计划显示引擎选择非聚集的“nci”索引,而不是聚集的索引。

在此处查看执行计划

我想知道为什么。

澄清:

对我来说,在这两种情况下,SQL 都读取了相同数量的数据。并且LoanNum在索引和 BTW 中,LoanNum是键的一部分,所以在我看来,如果它使用聚集索引更有意义。

索引与我发布的完全一样。当我捕获计划时,查询中有一些评论。您在帖子中看到的查询是正确的。我不想保留两个索引,我试图看看哪个表现更好,问题来了。

Pau*_*ite 7

优化器可以在两种主要策略之间进行选择:

  1. 扫描表(聚集索引)检查每一行以查看 LoanNum = 2712。
  2. 扫描和查找
    • 扫描非聚集索引以查找 LoanNum = 2712 的行
    • 查找非聚集索引未涵盖的匹配行的列数据。

关键是非聚集索引更小,所以扫描它预计会更便宜。这可能看起来违反直觉,因为聚集索引定义具有相同的键,并且非聚集索引包含列,但关键是聚集索引包含存储在行中的所有列- 聚集索引的叶(最低)级别字面上行内数据。

对于少量预期匹配项,扫描较小索引所节省的成本足以补偿键查找。

顺便说一下,您可能会发现WHERE 1 = 1从查询中删除会导致优化器选择聚集索引扫描。(冗余)常量到常量的比较可防止 SQL Server 参数化查询,因此估计值基于有关LoanNum 2712 的统计信息。如果查询是参数化的,SQL Server 将使用LoanNum值的平均分布,这可能会导致更多的预期行数和计划选择的更改。

也可以看看: