PhyOp_Range TBL: [dbo].[Child] Hints( READ-COMMITTEDLOCK FORCEDINDEX DETECT-SNAPSHOT-CONFLICT )很遗憾,目前执行计划中没有显示这一点。 我的相关文章:
错误信息提供了一般修复方法,SqlWorldWide在评论中建议了一个解决方案("使用可串行化隔离级别")。问题出在您的事务隔离级别上。解决方法是更改该事务的隔离级别。Microsoft在一篇名为教训1:理解可用的事务隔离级别的文章中对此进行了详细说明。
在该文章中,Microsoft在一个名为更新冲突的部分中声明:
还有一个并发问题尚未提及,因为它只适用于快照隔离级别。如果在快照隔离中读取了特定行(或行的版本),SQL Server保证在事务后期再次执行查询时会得到相同的行。但是,如果后续的查询是UPDATE或DELETE语句,并且该行自第一次读取以来已经发生了更改,会发生什么情况呢?SQL Server不能使用当前版本的行作为更新的基础,因为这将违反快照事务活动期间行不变的承诺。而且它也不能使用快照事务使用的行版本作为基础,因为更新或删除该行的其他事务将遇到丢失更新(这在SQL Server中是不允许或支持的)。相反,快照事务将被回滚,并收到以下错误消息:
Msg 3960, Level 16, State 4, Line 1 由于更新冲突,快照隔离事务被中止。您不能直接或间接使用快照隔离访问数据库'TestDatabase'中的表'Test.TestTran'来更新、删除或插入已被其他事务修改或删除的行。请重试事务或更改更新/删除语句的隔离级别。
这与您收到的错误类似。
我在推测,但我认为这就是正在发生的事情。当你删除父行时,引擎需要执行外键中定义的任何ON DELETE
规则——无论你是否已经知道自己已经删除了所有子行,引擎没有办法知道这一点。由于你所说的,在子表中外键列上没有索引(出于性能原因),引擎会使用聚集索引扫描(我假设你在子表中有一个主键),一旦它遇到第一行过期的行,就会中止事务,因为它无法了解插入在它查看的快照之外的外键值。
如果你在子表的外键列上建立了索引,服务器将能够有选择地访问可能受影响的行,也就是没有行(因为你那时已经删除了它们),从而避免快照冲突和聚集索引扫描。