为何SQL Server中覆盖索引的持久化计算列未被UPDATE操作使用?
核心原因
SQL Server查询优化器默认会在编译阶段将持久化计算列的引用自动展开为其原始定义的表达式,这一行为优先级高于匹配存储了计算结果的非聚集索引。
你场景中的ComputedColumn定义为[VarcharValue1] + [VarcharValue2],当你在UPDATE语句中引用c.ComputedColumn时,优化器会优先把这个引用替换为等价的c.VarcharValue1 + c.VarcharValue2表达式,此时它判定执行这个表达式需要获取VarcharValue1和VarcharValue2两个字段,而你的非聚集索引IX_MyTable_VarcharValue1_ComputedColumn中没有存储VarcharValue2字段,因此就触发了键查找回到聚集索引取数。
哪怕你的非聚集索引已经存储了持久化计算列的现成结果,优化器也不会优先匹配这个索引,这是SQL Server计算列处理的固有逻辑。该设计的初衷是为了应对计算列定义变更但索引未同步更新的极端场景,避免返回错误结果,但大部分常规场景下会带来不必要的性能开销。
解决方案
你可以选择以下任意一种方式让优化器优先使用你的覆盖索引,避免键查找:
- 开启跟踪标记176,阻止优化器展开持久化计算列的定义:
给UPDATE语句加上对应查询提示即可:
开启后优化器会把持久化计算列当作普通存储字段处理,直接匹配到包含该列的覆盖索引。UPDATE m SET m.NotComputedColumn = c.ComputedColumn FROM MyTable m JOIN ComputedColumnTable c ON m.VarcharValue1 = c.VarcharValue1 OPTION (QUERYTRACEON 176) - 强制指定使用目标非聚集索引:
在表名后添加索引提示:JOIN ComputedColumnTable c WITH (INDEX(IX_MyTable_VarcharValue1_ComputedColumn)) - 调整非聚集索引定义,将
VarcharValue2加入包含列:
这样就算优化器展开了计算列表达式,也能直接从非聚集索引取到需要的两个原始字段,不需要回表查询。CREATE NONCLUSTERED INDEX IX_MyTable_VarcharValue1_ComputedColumn ON dbo.ComputedColumnTable ([VarcharValue1] ASC, [ComputedColumn] ASC) INCLUDE (VarcharValue2);
内容的提问来源于stack exchange,提问作者684165342
相关产品推荐
相关产品推荐

