SQL Server 为啥不使用索引

Posted

技术标签:

【中文标题】SQL Server 为啥不使用索引【英文标题】:SQL Server why index is not usedSQL Server 为什么不使用索引 【发布时间】:2011-12-01 13:50:39 【问题描述】:

我在 SQL Server 2008 数据库中有一个下表:

CREATE TABLE [dbo].[Actions](
    [ActionId] [int] IDENTITY(1,1) NOT NULL,    
    [ActionTypeId] [int] NOT NULL,
    [Name] [nvarchar](50) NOT NULL,
    [Description] [nvarchar](1000) NOT NULL,
    [Comment] [nvarchar](500) NOT NULL,
    [Created] [datetime] NOT NULL,
    [Executed] [datetime] NULL,
    [DisplayText] [nvarchar](1000) NULL,    
    [ExecutedBy] [int] NULL,
    [Result] [int] NULL
)   
CONSTRAINT [PK_Actions] PRIMARY KEY CLUSTERED 
(
    [CaseActionId] ASC
)
) ON [PRIMARY]

GO


CREATE NONCLUSTERED INDEX [IX_Actions_Executed] ON [dbo].[Actions] 
(
    [Executed] ASC,
    [ExecutedBy] ASC
)

执行日期等于“2500-01-01”的 20 000 行和执行日期

当我执行查询时

select CaseActionId, Executed, ExecutedBy, DisplayText from CaseActions
where Executed='2500-01-01'  

查询计划显示对 PK_Actions 执行了聚集索引扫描,并且根本没有使用索引 IX_Actions_Executed

有趣的是我错过了索引提示

/* The Query Processor estimates that implementing the following index could improve the query cost by 99.9901%.
*/

CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[Actions] ([Executed])

但是索引已经存在了。

如果要选择 5% 的数据,为什么不使用索引?

【问题讨论】:

【参考方案1】:

最有可能的是,查询优化器只看到您也在选择 DisplayText - 因此对于在 NC 索引中找到的 20'000 行中的每一行,都需要进行 键查找 进入聚集索引以获取该数据 - 键查找是 昂贵 操作!因此,最终,立即扫描集群索引可能更容易、更有效。

我敢打赌,如果你在这里运行这个查询:

select CaseActionId, Executed, ExecutedBy
from CaseActions
where Executed='2500-01-01'

然后将使用 NC 索引

如果您确实需要 DisplayText 并且这是您会经常运行的查询,也许您应该将该列作为叶级的额外列包含在索引中:

DROP INDEX [IX_Actions_Executed] 

CREATE NONCLUSTERED INDEX [IX_Actions_Executed] 
ON [dbo].[Actions]([Executed] ASC, [ExecutedBy] ASC)
INCLUDE([DisplayText])

这将使您的 NC 索引成为覆盖索引,即它可以返回查询所需的所有列。如果您使用此覆盖索引再次运行原始查询,我很确定 SQL Server 的查询优化器确实会使用它。如果 NC 索引是一个覆盖索引,则使用任何 NC 索引的概率会显着增加,例如一些查询可以仅从 NC 索引中获取所需的所有列,而无需进行键查找。

缺少的索引提示有时会有点误导 - 还有一些已知的错误会导致 SQL Server Mgmt Studio 不断推荐已经存在的索引.....不要在这些上押太多钱索引提示!

【讨论】:

是的,您对 DisplayText 的看法是正确的。您说 20000 键查找比扫描整个聚集索引更昂贵?好的,谢谢你的信息。 @WaldiMen:测试一下! :-) 您可以通过WITH (INDEX(IX_Actions_Executed)) 强制 SQL Server 使用索引 - 比较执行计划、执行时间和 I/O 统计信息。另外:尝试 covering index 方法 - 如果包含 DisplayText 列,我很确定它会被使用....

以上是关于SQL Server 为啥不使用索引的主要内容,如果未能解决你的问题,请参考以下文章

为啥 SQL Server 不使用计算列上的索引?

为啥 SQL Server 全文搜索不匹配数字?

在SQL Server中为啥不建议使用Not In子查询

为啥当 WHERE 子句包含参数化值时 SQL Server 使用索引扫描而不是索引查找

为啥 SQL Server 在“选择 *”操作中在聚簇 PK 上使用非聚簇索引?

Sql server 创建索引后,只有查询后重建才会生效,不知为啥?