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

TN.*_*TN. 12 t-sql sql-server transactions transaction-isolation snapshot-isolation

以下陈述:

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

给我这个SQL错误3960:

由于更新冲突,快照隔离事务中止.您不能使用快照隔离直接或间接访问数据库'myDatabase'中的表'dbo.Companies'来更新,删除或插入已被其他事务修改或删除的行.重试事务或更改update/delete语句的隔离级别.

据我所知,从错误消息中,我不应该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)
Run Code Online (Sandbox Code Playgroud)

第二次更新正在做:

UPDATE dbo.Companies WHERE [Id] = @1 SET [Name] = @2;
Run Code Online (Sandbox Code Playgroud)

Bri*_*Low 7

看来SQL Server 将获取它必须读取的任何记录的更新锁,即使它没有修改它.

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

没有CustomerContactPerson上的支持索引,声明

从ContactPerson删除WHERE ID = @ID;

将需要对CustomerContactPerson中的所有行进行"当前"读取,以确保没有引用已删除的ContactPerson行的CustomerContactPerson行.使用索引,DELETE可以确定CustomerContactPerson中没有相关行,而无需读取受其他事务影响的行.

此外,在快照事务中,用于读取要转换和更新的数据的模式是在读取时采用UPDLOCK.这可确保您根据"当前"数据进行更新,而不是"一致"(快照)数据,并且当您发出DML时,数据将不会被锁定,并且您不会无意中覆盖另一场会议的变化.

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

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

  • 通过(长)dbaspot线程读取,听起来这是SQL Server的限制.此答案中的索引仅在两个参数涉及不同客户的情况下才有用. (2认同)