以下声明:
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;
答案 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相同并自动重试...