2

我有一个tbl_event使用非聚集索引调用的数据库表IDX_Event_FolderIDX_Event_Time定义为:

CREATE NONCLUSTERED INDEX [IDX_Event_Folder] 
ON [dbo].[tbl_event]([nobjectid] ASC)

CREATE NONCLUSTERED INDEX [IDX_Event_Time] 
ON [dbo].[tbl_event]([tetime] ASC)

我运行了以下简单查询,并直接在下面显示了执行计划:

查询一:

SELECT * 
FROM tbl_event 
WHERE tbl_event.nobjectid = 1410000
ORDER BY tetime

查询 1 的执行计划

查询 2:

SELECT * 
FROM tbl_event 
WHERE tbl_event.nobjectid = 1410000

查询 2 的执行计划

我的问题是,为什么nobjectid上的索引从未使用过?当在这些 select 语句的 where 子句中指定 nobjectid 时,我希望会有索引查找或扫描。我对这个分析的理解不正确吗?

4

3 回答 3

3

为什么 nobjectid 上的索引从未使用过?当在 where 子句中指定 nobjectid 时,我希望会有索引查找或扫描

一个普遍的误解!

一点是:由于您使用的是SELECT *,因此您需要表中的所有数据。所以最后,SQL Server 必须回到实际的数据页并获取所有值。

当发生索引查找并找到命中时,在这种情况下,SQL Server 必须进行书签查找——这是一项相当昂贵的操作。

而且由于这些操作相当昂贵,SQL Server 会尽量避免它们 - 所以在许多情况下,将使用表扫描,因为最后,这比查找 nc 索引然后进行书签查找要快.

检查点:

  • 该列的选择性如何nobjectid?这听起来像是一个或多或少的唯一 ID - 那会很好。如果您碰巧在列上的索引不是很有选择性,那么查询优化器通常会忽略它(因为它必须已经检查了太多行,所以最后表扫描会更快)

  • 表中有多少行??对于小表(少于几千行),从一开始就进行表扫描通常要快得多

此外,从您使用“RID 堆查找”的第一个执行计划开始,我会得出结论,您的表上没有聚集索引 -立即添加一个!没有聚集键(因此有一个而不是聚集表)也会减慢许多操作并降低非聚集索引的有效性。

尝试在“NUSE”列上添加聚集索引:

  • 狭窄
  • 独特的
  • 稳定的
  • 不断增加

INT IDENTITY是一个完美的候选 -UNIQUEIDENTIFIER或者一个非常广泛的复合列是最差的。在Kimberly Tripp 的博客上阅读有关选择正确聚集索引的所有内容

于 2011-02-09T18:18:31.050 回答
2

您在评论中说,表中当前有 18325170 行,其中只有大约 30 行具有 nobjectid=1410000。

即使您的IDX_Event_Folder索引被禁用,我也无法相信 SQL Server 会为该数量的行选择此计划,并且线条粗细表明它认为它可能正在处理 1 行而不是 18325170!

计划

所以我很确定您必须禁用自动更新统计信息?如果是这样,您将需要手动更新统计信息(或者最好启用此选项)

于 2011-02-09T20:46:18.437 回答
1

有几件重叠的事情正在发生

  • 你的SELECT *意思是“给我所有的专栏”。优化器认为扫描表更有用。
  • 唯一有用的索引是用于排序的 tetime 索引。它执行此操作,然后钻入表中。
  • 您没有聚集索引(RID/堆查找表明)
  • nobjectid 索引缺乏唯一性无济于事:这意味着全索引扫描。o 为什么在与 结合使用时使用索引 SELECT *

如果你有这个,那么索引将被使用(作为扫描)

SELECT nobjectid
FROM tbl_event 
WHERE tbl_event.nobjectid = 1410000

或者这个,最有可能使用新索引

CREATE NONCLUSTERED INDEX [IDX_Co,bined] 
ON [dbo].[tbl_event]([nobjectid] ASC, tetime)

SELECT nobjectid, tetime
FROM tbl_event 
WHERE tbl_nobjectid = 1410000
ORDER BY tetime

我建议你阅读这些文章

这些文章也应该涵盖缺乏聚集索引

于 2011-02-09T18:18:58.807 回答