根据执行计划,SQL Server 中的索引列似乎不适用于基本查询

Chr*_*ett 7 index sql-server

免责声明:我不是 DBA。过去我从这个板上学到了一些我正在构建的东西。

我有一张谷歌分析会话开始时间表。我在每一列上都有一个索引。我想过滤两个日期之间开始的所有会话。下面的屏幕截图显示了查询和索引。

查询文本和索引属性

查询运行得很快,但我不相信它使用基于执行计划的索引,这两个都说缺少索引并显示表扫描而不是索引扫描:

执行

为什么?

是不是因为我搜索日期时间的方式?如果不是在日期之间查看,而是将其设置为等于日期,则执行计划会使用索引显示它:

使用索引

但这不仅仅是这个表或日期时间。这是一个不同的表,在 varchar 列上有一个索引:

元数据索引

对此的一个简单查询也告诉我我缺少索引:

缺少 md 索引

我难住了。

Jos*_*ell 10

如果您希望 SQL Server 对该特定查询使用该索引,则需要包含该session_id列。否则,对于它发现的每一行,它都必须在基表中进行键查找。它会选择对小结果集执行此操作,但是一旦超过一定数量的行(“临界点”),SQL Server 认为仅扫描整个基表会更有效。

您可以使用语法的INCLUDE子句CREATE INDEX来完成此操作:

CREATE NONCLUSTERED INDEX IX_Start_Time
ON GoogleAnalytics.SessionStartTimes (session_start_time)
INCLUDE (session_id);
Run Code Online (Sandbox Code Playgroud)


Cha*_*ace 7

让我们一步一步地分解正在发生的事情:

  • 编译器需要获取session_id每一行的值。session_id不在索引中,所以需要从基表中查找
  • 你的表没有聚集索引,它是一个堆表,每一行都使用它的 RID 定位。聚集索引需要一个键查找来代替
  • 所以编译器需要决定是先查索引再查基表,还是只从头到尾扫描基表。它根据统计直方图中的估计行数决定是否执行此操作。
  • 出于显而易见的原因,您的第一个查询比第二个查询的估计行数更多。Sp 编译器已经决定额外查找的成本是不值得的。

您有多种解决方案:

  • 放入session_id索引。这不必是键的一部分,它可以是一INCLUDE列。我不建议这样做,因为它会以堆的形式离开表。
  • 更改索引将聚集session_start_time。这意味着所有列都包含在索引中。请记住,聚集索引不是表的额外索引,它表。无论如何,它通常比堆表更有效。请注意,PK也没有必须聚集键。
  • 在 上创建聚集索引session_id。然后您的非聚集索引将自动包含session_id(因为那是行定位器)。你可能想让它成为一个 PK,我怀疑无论如何都应该如此。再次,它不具备成为PK。

您选择哪个选项取决于通常过滤或分组的列。如果此查询是表上唯一完成的查询,那么您将聚集在session_start_time.

  • 好的,所以创建一个非唯一的聚集索引。聚类本身并不意味着唯一(尽管情况经常如此) (3认同)