【问题标题】:SQL Server why there is still Key Lookup when index already covered all columns?SQL Server 为什么在索引已经覆盖所有列时仍然存在 Key Lookup?
【发布时间】:2014-11-12 16:44:32
【问题描述】:

我正在运行一个包含类似于以下查询的 SP:

 DECLARE my_cursor CURSOR FOR 
 SELECT TOP 5000 id
 FROM Table
 WHERE ColA= 1 AND ColB= ColC

我创建了一个非聚集索引,其中 id 是索引键列,包括 ColA、ColB 和 ColC。

现在,当我单独运行上述查询(不带光标行)时,我可以看到只执行了索引扫描(使用我创建的索引)。这很好,符合预期。

但是,当我运行 SP 并查看执行计划时,我惊讶地发现对于上面的查询,它执行了索引扫描和键查找(这也需要大量时间)。

我的理解是,只有在需要索引中未涵盖的一些额外列时,键查找才会发挥作用。但显然我已经包含了所有必要的列。

有人对此有解释吗?谢谢!

【问题讨论】:

  • 您运行的是什么版本的 SQL Server?
  • 我在 SQL 2012 中重新创建了您的确切示例,它只为我运行索引扫描。 ColB 和 ColC 中比较的列类型是什么?
  • ColA 是 tinyint,ColB 和 ColC 是 int。 id 是 Table 的 bigint 和 PK。
  • 当您右键单击 Key Lookup 并点击 Properties 时,Key Lookup 的输出列表中有什么?
  • 输出列表显示 Chk1002。

标签: sql sql-server database stored-procedures


【解决方案1】:

根本问题是您使用的游标选项不正确。

当您使用默认值声明游标时

DECLARE CURSOR crsr for SELECT * FROM TABLE

语法,SQL Server 在 tempdb 中创建一个临时表,其中包含该查询返回的所有行(或时间戳,如果数据集包含时间戳列)的校验和。这是由OPTIMISTIC option 指定的,这是未使用STATICFAST_FORWARD 选项定义的游标的默认值。

当您通过游标FETCH NEXT 时,它会将当前获取的行的校验和与存储在 tempdb 中的校验和进行比较。如果校验和不同,则表示该行本身被游标更改,这是无效的,并且该更新被拒绝。

如果您不打算修改游标查询结果中包含的数据(即不更改 TABLE),则在游标上设置 FAST_FORWARD 选项,它不会执行此额外的校验和步骤。

当然,最终的解决方案是避免使用游标,因为它们依赖临时表和同步数据访问,性能不佳。

【讨论】:

  • 嗯...这似乎是另一个问题的答案。对吗?
  • 不,我们聊过,这是根本问题。
  • 啊,好的。但是,就目前而言,原始问题与您的答案之间没有明显的联系。也许OP可以编辑他们的问题以至少包含对光标的引用,以便将来对其他人有用。但是你无法控制她/他做什么。 :)
  • @Ben Thui,感谢您调查此问题。我已经相应地编辑了问题。
  • @Kyle Hale,非常感谢您的帮助。我已经阅读了一些资料并尝试了一些技巧,包括您对 FAST_FORWARD 的建议……它确实有效!它现在只在执行计划中使用索引搜索。我也遇到过 INSENSITIVE 游标,它看起来也可以解决问题,只是它不如 FAST_FORWARD 高效,这与预期的一样。请问 FAST_FORWARD 是 READ_ONLY,默认情况下是否是 INSENSITIVE 的变体?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
  • 2016-08-22
  • 1970-01-01
相关资源
最近更新 更多