为什么SQL Server不使用我的索引?

Mar*_*als 23 sql-server indexing

在我们的数据库中,我们有200.000行的这个表

CREATE TABLE dbo.UserTask (
    UserTask_ID int NOT NULL IDENTITY (1, 1),
    UserTask_SequenceNumber int NOT NULL DEFAULT 0,
    UserTask_IdEntitat uniqueidentifier NOT NULL,
    UserTask_Subject varchar(100) NOT NULL,
    UserTask_Description varchar(500) NOT NULL,
            .....
            .....
    CONSTRAINT [PK_UserTask] PRIMARY KEY CLUSTERED 
    (
        [UserTask_ID] ASC
    ) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)

我在UserTask_IdEntitat列上创建了一个索引

CREATE NONCLUSTERED INDEX IX_UserTask_IDEntitat ON dbo.UserTask 
(
    UserTask_IDEntitat
)
Run Code Online (Sandbox Code Playgroud)

执行以下查询,执行计划向我们显示索引on UserTask_IDEntitat用于执行查询:

SELECT UserTask_ID
  FROM UserTask   
 WHERE UserTask_IdEntitat = @IdEntitat 
 ORDER BY UserTask_LastSendSystemDateTime desc
Run Code Online (Sandbox Code Playgroud)

但是如果我们在Select列表中添加另一列,则不使用索引

SELECT UserTask_ID, UserTask_SequenceNumber, UserTask_IDEntitat, ....., UserTask_Subject
  FROM UserTask   
 WHERE UserTask_IdEntitat = @IdEntitat 
 ORDER BY UserTask_LastSendSystemDateTime desc
Run Code Online (Sandbox Code Playgroud)

为什么添加与主键不同的列会导致SQL Server执行计划不使用UserTask_IDEntitat列上的索引?

在此链接http://bytes.com/topic/sql-server/answers/144592-sqlsever-not-using-index之后,似乎是在列上重复过滤值的次数,它可以使我没有使用索引,但我尝试使用重复60.000次的@IdEntitat值进行查询,而其他重复次数仅为175次且结果相同,IDEntitat则忽略列上的索引.

这让我抓狂!

谢谢你的帮助.

mar*_*c_s 50

好的 - 只要您只选择索引中的列或集群键中的某些内容(通常是主键),就会使用索引,因为SQL Server可以找到所需的所有信息(UserTask_IDEntitat列和索引导航结构的叶级别中的聚簇索引列.因此,它可以SELECT直接从索引的叶级页面返回该查询所需的数据.

但是:如果您需要选择第二列,既不是索引定义中的列,也不是集群键的一部分,那么SQL Server必须对实际数据页执行所谓的书签查找.

因此,对于它在非聚簇索引中找到的每一行,它必须获取聚簇索引值,搜索聚簇索引以在该聚簇索引的叶级别查找实际数据页,然后选择一列您想.

书签查找非常适合少量点击 - 如果你选择了数千行,它们对于性能来说是完全毁灭性的.在这种情况下,SQL Server查询优化器正确地使用聚簇索引扫描 - 因为在聚簇索引中,在叶级别上,它具有所有可用的行.

所以:如果你有一个索引UserTask_IDEntitat,你有时也需要第二列UserTask_SequenceNumber- 那么你可以在你的非聚集索引中包含该列:

CREATE NONCLUSTERED INDEX IX_UserTask_IDEntitat 
ON dbo.UserTask(UserTask_IDEntitat)
INCLUDE(UserTask_SequenceNumber)
Run Code Online (Sandbox Code Playgroud)

有了这个,那个附加列只存在于非聚集索引的叶级别中(它不能在WHERE子句中使用 - 它不是索引导航结构的一部分!) - 你的第二个SELECT可以再次满足非聚集索引的叶级节点 - >不需要昂贵的书签查找 - >您的索引将再次使用.

简而言之:除非您的非聚集索引具有高度选择性(例如,返回1%或更少的行),除非您的非聚簇索引是覆盖索引(包含满足特定查询所需的所有列的索引),否则更改非常高,SQL Server 不会使用你的非聚集索引.

有关更多信息:

  • @MarcCals:如果你需要**所有**列 - 那么大部分时间,索引都不会真正有用,因为进行聚簇索引扫描通常比执行数千个昂贵的书签查找更有效 (4认同)