SQL Server为何能在非聚集索引查找中使用聚集索引列作为查找谓词?
我发现了一个奇怪的现象,希望有人能提供相关文档或解释其实现原理:
我有一张包含Key1、UnexpectedClusteredKey、NonClusteredKey列的表,创建了聚集索引PK_Index(Key1, UnexpectedClusteredKey)和非聚集索引IX_Index(Key1, NonClusteredKey)。当执行包含所有三列过滤条件的SELECT查询时,实际执行计划显示使用了非聚集索引,且查找谓词包含全部三列。
我原本预期会使用聚集索引并通过残留谓词过滤NonClusteredKey,或是使用非聚集索引并通过残留谓词过滤UnexpectedClusteredKey(我明白无需键查找,因为两个索引都是覆盖索引)。这种能同时利用三列作为查找谓词的行为对我来说是新特性,我猜测这是因为UnexpectedClusteredKey是聚集索引中未包含在非聚集索引里的列,但找不到相关行为的官方说明。
测试代码
DROP TABLE test.tbl_IndexSeekTest CREATE TABLE test.tbl_IndexSeekTest ( Key1 INT, UnexpectedClusteredKey INT, NonClusteredKey INT ) CREATE CLUSTERED INDEX PK_Index on test.tbl_IndexSeekTest ( Key1, UnexpectedClusteredKey ) CREATE NONCLUSTERED INDEX IX_Index on test.tbl_IndexSeekTest ( Key1, NonClusteredKey ) INSERT INTO test.tbl_IndexSeekTest VALUES (1,1,1),(1,2,1),(1,3,1),(1,4,1),(1,5,1) SELECT * FROM test.tbl_IndexSeekTest WHERE Key1 = 1 AND NonClusteredKey = 1 AND UnexpectedClusteredKey = 1
原理解释
这是因为非聚集索引的叶节点会自动包含聚集索引的全部键列(即Key1和UnexpectedClusteredKey),作为行定位器来指向对应的聚集索引行。所以你的非聚集索引IX_Index实际的结构是:
- 显式索引键:
Key1、NonClusteredKey - 隐式包含列:
UnexpectedClusteredKey(聚集索引键)
当SQL Server生成执行计划时,查询优化器可以利用非聚集索引中所有可用的列构建查找谓词——因为UnexpectedClusteredKey已经存在于非聚集索引的叶节点中,不需要额外的键查找操作。优化器会评估两种索引的使用成本:
- 使用聚集索引
PK_Index时,需要先通过Key1+UnexpectedClusteredKey定位行,再通过残留谓词过滤NonClusteredKey - 使用非聚集索引
IX_Index时,可以直接用Key1+NonClusteredKey+UnexpectedClusteredKey作为查找谓词,精准定位符合所有条件的行,避免残留过滤,执行成本更低
这种行为是SQL Server索引的标准特性,官方文档明确说明:非聚集索引的叶级行包含完整的聚集键,用于唯一标识对应的行。查询优化器会自动识别这些隐式包含的聚集键列,并将其纳入谓词评估,以生成更高效的执行计划。
内容的提问来源于Stack Exchange,提问作者Fernando Rojo

