2016-08-02 201 views
0

I 認爲我遇到的問題是併發問題,但我想我不太瞭解SQL Server事務,以確定要做什麼。外鍵約束衝突併發問題

我有插入新行到一個表中的存儲過程,然後使用由INSERT創建的ID創建另一個表中的記錄,這對RID一個外鍵約束:

INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SET @RID=(SELECT MAX(RID) FROM tbl_Registrations WHERE ...); 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 

這個存儲過程是從網站上的用戶交互中調用的,每次我測試它時,它都可以正常工作。然而,每過一段時間,我會從我們的錯誤處理一封電子郵件,但以下情況除外:

System.Data.SqlClient.SqlException (0x80131904): The INSERT statement conflicted with the FOREIGN KEY constraint "FK_tbl_RegistrationCertificates_tbl_Registrations". 
The conflict occurred in ... table "dbo.tbl_Registrations", column 'RID'. 
The statement has been terminated. 

但是,無論是我的用戶在點擊「更新」按鈕,持續,直到他們看到變化,或者即使消息顯示The statement has been terminated,交易仍在進行,因爲使用來自電子郵件的其他信息,我可以看到這兩個表已實際更新。

我知道我不給一噸的信息在這裏,但我無法找到答案的大問題是:

  1. 是否在聲明的末尾分號T中做任何事 - SQL?我只注意到在INSERT語句結尾處沒有分號。 (我通常會讓他們擺脫習慣,但我正在開發其他人制造的系統。)
  2. 這對於瞭解SQL Server的人比我更瞭解的人是否看起來像是一個併發問題?
  3. 如果看起來像是一個併發問題,我可以使用事務來減輕它,我該怎麼做?

編輯:我只是意識到SP是不是什麼都拋出了錯誤。網站還有另一個地方做插入,而不檢查是否存在RID。不過,我很高興我提出了這些問題,因爲我仍然不知道其他答案。

回答

1

T-SQL將分號視爲語句分隔符,儘管並非總是需要。關於這一點的更深入的討論可以在question中找到。

至於外鍵錯誤,您提交的存儲過程可能會因爲幾個原因而使用一些改進。

首先,您很可能在此處使用交易。如果您不希望添加註冊記錄,除非還添加了註冊證書記錄,那麼整個操作應該在單個事務中。

其次,如果同時調用存儲過程,則選擇標識插入列的MAX值可能很容易成爲併發問題的來源。想象一下,在線程B INSERT之前,線程A INSERTS但無法運行其SELECT MAX(RID)的場景。現在線程A和B都會嘗試向tbl_RegistrationCertificates插入相同的值。

SCOPE_IDENTITY()函數將爲您提供此存儲過程中查詢生成的最新身份值的幫助。

一些更新的代碼:

BEGIN TRANSACTION 
INSERT INTO tbl_Registrations ... 

DECLARE @RID int; 
--get newly-created registration ID 
SELECT @RID = SCOPE_IDENTITY() 
INSERT INTO tbl_RegistrationCertificates VALUES (@RID, 9); 
COMMIT TRANSACTION 
+0

啊'SCOPE_IDENTITY()'是不是我以前見過;這非常有幫助。顯然,爲了簡潔起見,我留下了很多代碼,但是如果基於SP的參數滿足其他條件,則只會執行第二個「INSERT」。在這種情況下交易是否有意義? –

+0

還有一個問題:使用事務和批分隔符有什麼區別? 'GO'與'BEGIN TRANSACTION ... COMMIT TRANSACTION'? –

+1

事務邏輯上將多個語句分組在一起,以使它們成功或失敗。在我上面的代碼中,如果由於某種原因'INSERT' to _tbl_RegistrationCertificates_失敗,那麼'INSERT' to _tbl_Registrations_也會回滾。當一組報表全部被視爲一個操作(BEGIN'和'COMMIT'之間的所有內容)時,事務可幫助您保持邏輯上一致的狀態。另一方面,「GO」只是告訴SQL Server工具運行一組語句。 –