Sql-Server

在 SQL Server 中,用外行的話來說,如何將資源鎖定到足以執行 INSERT-IF-NOT-EXISTS 事務的程度?

  • August 14, 2020

如果有人問如何INSERT-IF-NOT-EXISTS在 SQL Server 中執行操作,他們通常會得到這樣的答案:

IF NOT EXISTS(SELECT 1 FROM [TheTable] WHERE [ColumnX] = @valX)
   INSERT [TheTable] ([ColumnX]) VALUES (@valX)

我看到的問題是在SELECT聲明和INSERT聲明之間,情況可能會在外部發生變化。另一個程序可以在語句之後語句之前插入ColumnX值,從而導致引發錯誤。SELECT``INSERT

我在軟體領域工作了一段時間,但不是數據庫專家,當我在 SQL Server 中搜尋此問題的答案時,我看到的結果要麼無關緊要,要麼很難真正應用(因為他們’要麼回答不同的問題,要麼按照數據庫專家量身定制的術語編寫)。

那麼通俗的講,你是如何解決這個問題的呢? 在最近的歷史中,我確實對 SQL 有點生疏,但我認為確實應該有一個實用的鎖定機制來用於此(無論是否存在)。作為備份,也許錯誤處理可以專門確定引發的錯誤是否與這個確切的問題相匹配,在這種特定情況下忽略它。

最好這不涉及每次都鎖定整個表。

選項 1:取出至少鎖定該行將存在的索引中的範圍的鎖定。

SET XACT_ABORT ON;
BEGIN TRAN
IF NOT EXISTS(SELECT 1 FROM [TheTable] WITH (UPDLOCK, HOLDLOCK) WHERE [ColumnX] = @valX)
   INSERT [TheTable] ([ColumnX]) VALUES (@valX)
COMMIT

HOLDLOCK將給出可序列化的語義並鎖定索引中現有鍵之間的範圍,該值適合(如果有合適的索引,否則它將鎖定整個表)。UPDLOCK減少了這種模式下死鎖的可能性,因為兩個並發查詢在讀取階段不能取出相同的範圍鎖。

選項2:您可以只添加一個唯一約束ColumnX並嘗試插入並擷取重複鍵違規引發的錯誤。

鑑於選項 1ColumnX無論如何都需要一個帶有前導列的索引來滿足您不“每次都鎖定整個表”的偏好,您不妨添加一個並將其定義為唯一的。無論如何,索引都會加快存在檢查。有了這個,我會根據我期望嘗試插入重複項的頻率在選項 1 和 2 之間進行選擇。

引用自:https://dba.stackexchange.com/questions/273765