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