26

在我们的数据库中,我们有这个有 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
)

执行以下查询,执行计划向我们显示 index onUserTask_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似乎过滤值在列上重复的次数,它可以使未使用索引,但我尝试使用重复 60.000 次的 @IdEntitat 值和仅重复 175 次的其他值进行查询,结果相同,IDEntitat列上的索引被忽略。

这让我抓狂!!!

谢谢你的帮助。

4

3 回答 3

65

好的 - 只要您只选择索引中的列,或者从集群键(通常是主键)中选择某些内容,那么将使用索引,因为 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不会使用您的非聚集索引的情况非常高。

欲了解更多信息

于 2013-07-25T14:42:21.993 回答
2

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

于 2013-07-25T13:18:14.557 回答
0

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

此外,此索引在 INCLUDE 中没有所有字段,最好的解决方案是将所有必要的选定字段添加到索引 INCLUDE。就我而言,免费缓存有帮助。

DBCC freeproccache

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

于 2019-07-17T12:14:33.197 回答