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

时间:2013-07-25 13:08:22

标签: 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]

我已使用

UserTask_IdEntitat列上创建了索引
CREATE NONCLUSTERED INDEX IX_UserTask_IDEntitat ON dbo.UserTask 
(
    UserTask_IDEntitat
)

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

SELECT UserTask_ID
  FROM UserTask   
 WHERE UserTask_IdEntitat = @IdEntitat 
 ORDER BY UserTask_LastSendSystemDateTime desc

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

SELECT UserTask_ID, UserTask_SequenceNumber, UserTask_IDEntitat, ....., UserTask_Subject
  FROM UserTask   
 WHERE UserTask_IdEntitat = @IdEntitat 
 ORDER BY UserTask_LastSendSystemDateTime desc

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

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

这让我发疯了!!!

感谢您的帮助。

3 个答案:

答案 0 :(得分:45)

好的 - 只要您只选择索引中的列,或者选择群集键中的某些内容(通常是主键),就会使用索引,因为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)

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

长话短说,除非您的非聚集索引 高度选择性 (例如,返回1%或更少的行),除非您的非聚集索引是覆盖索引(包含满足特定查询所需的所有列的索引),然后SQL Server将 NOT 使用非聚簇索引的更改非常高

了解更多信息

答案 1 :(得分:2)

您可以使用查询中的查询提示来使用Index。以下是更多详细信息的链接: http://msdn.microsoft.com/en-us/library/ms181714.aspx

答案 2 :(得分:0)

当相同的查询在不同的数据库上制定不同的计划时,我遇到了这种情况。在一个数据库上,它使用非聚集索引;在另一个数据库上,它使用表扫描。

此索引在INCLUDE中也不包含所有字段,最好的解决方案是将所有必需的选定字段添加到索引INCLUDE中。就我而言,空投缓存很有帮助。

DBCC freeproccache

有时,如果查询计划生成器的碎片超过50%,则它会忽略索引,因为查找索引中的行要比扫描整个表花费更多的时间。