根据上篇知道非聚集索引不能覆盖表中所有的列。假设在非聚集索引关键字中有一个带有谓词的查询用来选择没有被索引覆盖的列。如果SQL Server在非聚集索引中进行查找,会丢掉一些必须的列。反之,如果在聚集索引中进行查找,会丢掉一些必须的列。反之,如果在聚集索引中进行扫描,则会获取所有的列。反之,如果在聚集索引中进行扫描,则会获取所有的列。但这要涉及表中的每一行,从而影响效率。例如下面这个查询。
这个查询与之前阐述索引查找所有的查询一样,但这个查询选择了这样两列:OrderId与CustomerId。非聚集索引OrderDate只覆盖了OrderId列。
针对这个问题,SQL Server提供的解决方案。对于每一个从非聚集索引取回的行都可以查找聚集索引中剩余行的值(例如示例中的行CustomerId)。把这个操作称之为“bookmark lookup”。书签指向堆或聚集索引中的行。SQL Server严格地为非聚集索引中的每一行都存储了书签。这样,在基本表中就可以一直找到非聚集索引所对应的行。
Bookup lookup可以与堆一起使用,就像上面所提到的可以与聚集索引一起使用一样。在SQL Server 2000中,堆上的Bookmark lookup与聚集索引上的一样。在SQL Server 2005中,堆上的bookup lookup与聚集索引的一样。堆上的bookmark lookup继续使用嵌套循环连接,而是用RID looup运算符来代替聚集索引。RID lookup运算符包括堆bookmark lookup上的查找谓词,但堆不是索引,RID lookup也不是索引查找。
Bookmark lookup不是简单的运算符。假设非聚集索引关键字与聚集索引关键字不存在如何关联,每个Bookmark lookup执行随机的I/O操作到聚集索引或堆中。随机I/O的成本很高。当比较各种执行计划的替代品时,包括扫描、查找及带有bookmark lookup的查找,优化器必须确定在执行更多的顺序I/O时是否能话费更少的成本,并使用覆盖所有需求列的索引扫描来搜索更多的行;或执行更少的随机I/O并使用带有多个选择性的谓词和bookmark lookup的查找。因为随意I/O比顺序I/O要消耗更多的成本,所以聚集索引扫描比带有bookmark look索引查找更省事。
由此我们可以得出结论,在查询列表中的选择列,尽可能的是查找谓词中索引覆盖的列,这样可以直接返回结果,不需要bookmark lookup,避免了整体扫描聚集索引!