【问题标题】:Why is SQL Server not using Index for very similar datetime query?为什么 SQL Server 不使用索引进行非常相似的日期时间查询?
【发布时间】:2012-10-02 12:20:51
【问题描述】:

我在 SQL Server 上有一个大约 100 万行的表。 它有一个 ID (PK)、一个状态 (int) 和一个日期时间列。 我还在日期时间列上创建了一个索引。

现在我发现了一个我不明白的效果。

SELECT status
FROM table
WHERE dateTime BETWEEN '2010-01-01T00:00:00' AND '2010-01-02T12:00:00'

此语句返回 3664 行。它运行大约 150 毫秒,执行计划显示它正在使用键查找进行索引查找。

现在,如果我将其更改如下(只需将小时从 12 更改为 13):

SELECT status
FROM table
WHERE dateTime BETWEEN '2010-01-01T00:00:00' AND '2010-01-02T13:00:00'

此语句返回 3667 行。它运行了大约 600 毫秒,执行计划显示它使用主键!

我只是不明白。对于 3667 及更多行,它始终使用主键,即使查找速度更快。

有解释吗?

【问题讨论】:

  • 尝试在日期时间字段的索引中包含status 列-我几乎可以保证这样做,查询优化器将使用该索引并且比执行聚集索引扫描时快得多。 ...

标签: sql indexing primary-key


【解决方案1】:

status 不包含在datetime 的索引中,因此它需要对每个匹配行进行键查找以检索此值。

随着范围的增长(因此需要查找的数量),它估计仅扫描整个(覆盖)聚集索引以避免查找会更快。在您的情况下可能不正确。它从一个计划切换到另一个计划的点称为the tipping point

您应该检查估计的行数与实际的行数是否有问题(也许自上次更新统计信息以来,一些与范围匹配的行已被删除)。

或者由于高度碎片或由于某些其他原因,costing assumptions 所做的索引扫描可能比成本假设假设的要高,这并不能反映您环境中的实际相对性能。

【讨论】:

  • @pst - 因为时间范围越宽,估计的行数就越大,因此达到临界点。执行 CI 扫描的查询包括一个额外的小时。
  • 在创建新索引之前,我已经删除了所有其他索引和统计信息。确实存在一个“临界点”(在本例中为“2010-01-02T13:00:00”)。对于每个较大的时期,它总是使用主键,这使得它明显变慢。不过,我不明白,因为索引查找应该更快,因为主键总是必须梳理每一行,对吧?
  • @Powerslave - 它假定执行(主要是顺序 IO)扫描比大量随机 IO 查找更快,并相应地花费两个选项。它可以在桌子的一小部分从一个切换到另一个。在我的回答中的引爆点链接中对此进行了更多讨论。成本假设假设查询将使用冷缓存执行,因此所有数据都需要从磁盘导入。
  • 我读了你的文章,得到了一点启发。现在我知道,临界点确实是我的问题,覆盖索引不会在临界点运行。但我注意到其他一些问题。当我为日期时间条件使用参数时,将永远不会使用日期时间上的索引(无覆盖),它始终使用主键(聚集索引扫描)。但是我们不应该使用 always 参数吗?您现在如何解释呢?
  • @Powerslave - 您使用的是参数还是变量?解释会有所不同,具体取决于您使用的是哪一个,但对于将OPTION (RECOMPILE) 添加到查询中都会使其考虑到传递的实际值。如果您正在使用参数,那么由于参数嗅探,计划根据传递的第一个参数值进行编译并重用于其他调用。如果它是变量,那么它根本不查看值,因为在分配变量之前编译语句。#
猜你喜欢
  • 1970-01-01
  • 2013-10-31
  • 2022-08-14
  • 2014-10-29
  • 1970-01-01
  • 1970-01-01
  • 2019-07-14
  • 2014-02-06
  • 1970-01-01
相关资源
最近更新 更多