【问题标题】:SQL Server non-clustered Index never shows up in execution planSQL Server 非聚集索引永远不会出现在执行计划中
【发布时间】:2011-06-24 08:11:29
【问题描述】:

我有一个名为 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)

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

查询 1:

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

查询 2:

SELECT * 
FROM tbl_event 
WHERE tbl_event.nobjectid = 1410000

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

【问题讨论】:

  • 能否请您发布包含(!)主键定义的表定义?
  • 表格有多少行?其中有多少具有1410000 的值?
  • 表中当前有18325170行,其中只有大约30行nobjectid=1410000
  • 好的,这对我来说绝对是非常奇怪的,索引不会被使用。具有 1:610839 的选择性。你确定它没有被禁用?您可以在sys.indexes 中看到这一点。您是否还打开了自动创建统计信息和自动更新统计信息?另外,如果您尝试强制解决问题怎么办SELECT * FROM tbl_event WITH (INDEX = IDX_Event_Folder) WHERE tbl_event.nobjectid = 1410000

标签: sql-server indexing sql-execution-plan query-performance


【解决方案1】:

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

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

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

【讨论】:

  • 这就是问题所在。现在,当我运行选择查询时,我看到 IDX_Event_Folder 上的索引扫描。
  • @mjh41 - 希望索引搜索(以及一些 RID 查找)?
  • 是的,你是对的 - 索引查找(非扫描)的成本为 5%,而 RID 查找的成本为 95%
【解决方案2】:

有几个重叠的事情发生

  • 您的SELECT * 表示“给我所有列”。优化器认为扫描表更有用。
  • 唯一有用的索引是用于订购的时间。它会执行此操作,然后钻入表中。
  • 您没有聚集索引(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

我建议你阅读这些文章

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

【讨论】:

    【解决方案3】:

    为什么 nobjectid 上的索引永远不会 利用?我希望会有 nobjectid 时的索引查找或扫描 在where子句中指定

    一个常见的误解!

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

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

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

    检查要点:

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

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

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

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

    • 独一无二的
    • 稳定
    • 不断增加

    INT IDENTITY 是一个完美的候选 - UNIQUEIDENTIFIER 或非常宽的复合列集是最差的。在Kimberly Tripp's blog

    阅读有关选择正确聚集索引的所有内容

    【讨论】:

    • 加:有主键吗?
    • 此表上没有主键。此表中的记录与由 nobjectid 标识的对象相关联。对于 1 个对象,tbl_event 中可以有 n 条记录。
    • @mjh41: 如果它没有主键,它就不是表 - 向该表添加一个好的主键,并使其成为一个好的集群键,也!
    • OP 说表中有 18325170 行,只有 30 行匹配谓词...
    • @Martin: true - 这听起来确实很有选择性,应该使用索引;看来你的预感是正确的(再次!)
    猜你喜欢
    • 1970-01-01
    • 2020-04-15
    • 2011-10-07
    • 2012-10-01
    • 2018-05-08
    • 1970-01-01
    • 2011-05-13
    • 2014-04-27
    相关资源
    最近更新 更多