由于更新冲突,快照隔离事务中止

时间:2012-05-23 11:05:20

标签: sql-server tsql transactions transaction-isolation snapshot-isolation

以下声明:

INSERT INTO dbo.Changes([Content], [Date], [UserId], [CompanyId]) 
  VALUES (@1, @2, @3, @4);
SELECT @@identity;

给了我这个SQL错误3960:

  

由于更新冲突导致快照隔离事务中止。您   无法使用快照隔离直接访问表'dbo.Companies'   或间接在数据库'myDatabase'中更新,删除或插入   已被另一个事务修改或删除的行。   重试事务或更改隔离级别   更新/删除声明。

据我所知,从错误消息中,我不应在另一个连接修改dbo.Companies期间更新,删除或插入表dbo.Companies

但是当我将一个新行插入到另一个表dbo.Changes(具有dbo.Companies的外键)并且我没有删除dbo.Companies中引用的行时,为什么会出现这种情况,但是我刚刚更新dbo.Companies中的行而不是主键?这应该可行,不应该吗? (这是SQL Server中的错误吗?)

更新:

表格如下:

dbo.Changes([Id] int PK, [Content] nvarchar, 
  [Date] datetime, [UserId] int, [CompanyId] int -> dbo.Companies.[Id])
dbo.Companies([Id] int PK, [Name] nvarchar)

第二次更新正在进行:

UPDATE dbo.Companies WHERE [Id] = @1 SET [Name] = @2;

2 个答案:

答案 0 :(得分:6)

似乎SQL Server 将获取任何必须阅读的记录>,即使它没有修改

有关此microsoft.public.sqlserver.server thread的更多信息:

  

没有CustomerContactPerson的支持索引,声明

     

DELETE FROM ContactPerson WHERE ID = @ID;

     

需要“当前”   读取CustomerContactPerson中的所有行以确保存在   没有引用已删除的CustomerContactPerson行   ContactPerson行。使用索引,DELETE可以确定   如果没有阅读,CustomerContactPerson中没有相关的行   受其他事务影响的行。

     

此外,在快照中   事务处理用于读取您要转向的数据的模式   周围和更新是在你阅读时采取UPDLOCK。这确保了   您是否根据“当前”数据进行更新,而不是   “一致”(快照)数据,以及当您发布DML时,它就是   数据不会被锁定,您也不会无意中覆盖另一个数据   会议的变化。

我们的修复是向外键添加索引

在您的示例中,我怀疑为Changes.CompanyId添加索引会有所帮助。我不确定这是否真的是一个解决方案。 SQL Server优化器可以选择不使用索引吗?

答案 1 :(得分:3)

SQL Server可以看到对依赖表的更新,可以修改插入行为...对我来说似乎很公平,因为SQL无法猜测其他逻辑可能依赖于[name]列(触发器等)

如果您的应用程序实现死锁重试逻辑,您可以修改它们以将错误号3960视为与错误号1205相同并自动重试...