10

如下声明:快照隔离事务中止由于更新冲突

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中的错误?)

UPDATE:

表看起来如下:

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; 

回答

5

看来SQL服务器将在其必须读取的任何记录上获取更新锁即使它不修改它。在此microsoft.public.sqlserver.server thread

更多信息:

没有上CustomerContactPerson支撑指数,语句

DELETE FROM ContactPerson WHERE ID = @ID;

将需要一个“当前” 读中CustomerContactPerson所有行,以确保有 是指删除 ContactPerson行没有CustomerContactPerson行。使用索引,DELETE可以确定 CustomerContactPerson中没有相关的行,但未读取受其他事务影响的 行。

此外,在快照 事务中,读取数据的模式(您将要打开 并更新)是在读取时采用UPDLOCK。这确保了 您正在根据“当前”数据进行更新,而不是 “一致”(快照)数据,并且当您发出DML时,它将不会锁定数据, t不知不觉中会覆盖另一个会话的更改 。

我们整个解决方案添加索引的外键

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

+0

谢谢你的提示,但它似乎没有帮助。 –

+0

这两个语句(INSERT和UPDATE)是指同一个客户?如果是这样,就我所知,我们运气不好。 –

+0

是的,插入正在使用当前正在更新的公司的ID。 –

3

SQL Server可以看到对可能会修改插入行为的从属表进行更新...对我来说看起来很公平,因为SQL无法猜测其他逻辑可能依赖于哪个o n中的[名]列(触发器等)

,如果你的应用程序中实现死锁重试逻辑,你可以修改它们治疗错误没有3960一样的错误没有1205和自动重试...

+0

你说得很好......除了我仍然不明白,为什么在相关表的外键列上有一个非聚集的,非唯一索引就足以使这不会发生 - 即不是这种情况即使使用索引,它仍然可以修改插入的行为? – Kram

+0

@Kram我想这个指数有助于粒度。引擎知道一个依赖表需要被锁定,但没有索引,它可以尝试锁定整个表而不是一行 – jean