2011-05-25 251 views
7

互聯網上有很多關於這個常見「問題」的信息。Sql Server 2005 - 插入如果不存在

解決方案,如:

IF NOT EXISTS() BEGIN INSERT INTO (...) END 

不是線程安全的在我看來,你可能會同意。

但是,您是否可以確認將存在放入單個select的where子句中可以解決sql引擎中併發性最高的問題? 夠了嗎?

insert into Table (columns) 
select column1, column2, column3 
where not exists (select top 1 1 from Table where something) 

應該在那裏也加入了一些更高的事務級或 可以這樣在一個默認的執行:承諾?

這項工作在未提交的水平下?

謝謝!

//後來添加

我可以假設兩個SQL」是正確的:

1) 組事務隔離級別重複的讀取

IF NOT EXISTS() BEGIN INSERT INTO (...) END 

2)事務隔離級別設置重複讀

insert into Table (columns) 
select column1, column2, column3 
where not exists (select top 1 1 from Table where something) 
+1

沒有也不會在任READCOMMITTED或工作未提交的級別。您需要一些額外的鎖定提示。 [只有插入一行,如果它不在那裏](http:// stackoverflow。com/questions/3407857 /只插入一行 - 如果它不是已經存在的) – 2011-05-25 07:24:25

+1

你不需要「幫忙」存在子句 - 它們足夠聰明,可以在他們看完之後完成1排。你可以執行'EXISTS(SELECT * FROM ...'並且它做的是正確的事情。 – 2011-05-25 07:26:14

+0

它會做正確的事情,但是對於cocurrency而言它不是線程安全的,並且在高的情況下可能會發生主要違規錯誤因此根據@Martin的鏈接,應該添加可重複的讀取隔離事務 – Paul 2011-05-25 07:35:57

回答

6

使用TRY/CAT CH就可避免額外讀

BEGIN TRY 
    INSERT etc 
END TRY 
BEGIN CATCH 
    IF ERROR_NUMBER() <> 2627 
     RAISERROR etc 
END CATCH 
  • 一個NOT EXISTS將讀取表,插入無論是在IF或WHERE
  • 需要讀來檢查的獨特性

如果可以的話丟棄重複,這是一種高度可擴展的技術

鏈接:

+0

好吧,但如果我應該插入或更新然後:我可以這樣做:如果error_number()= 2627開始更新...結束? – Paul 2011-05-25 07:52:13

+0

@Paul:查看我的最後一個鏈接,然後處理UPDATE – gbn 2011-05-25 07:53:59

+0

是否有可能在啓動catch塊時引發默認錯誤?或者我必須爆炸獲取error_number和消息來滿足raiserror參數? – Paul 2011-05-25 09:28:08

1

要回答這個問題,更新將repeatable read仍然是不夠的。

這是您需要的holdlock/serializable級別。

你正試圖阻止phantoms(其中第一閱讀沒有行符合標準,因此NOT EXISTS返回true,但隨後併發事務插入一行滿足它)

+0

看來,我會開始在編寫sql語句時偏執狂:) – Paul 2011-05-25 08:09:52

+0

@Paul - 'serializable'也會給你造成死鎖的風險,這可以通過'UPDLOCK,HOLDLOCK'來避免。儘管我自己會使用'TRY ... CATCH'。 – 2011-05-25 08:18:57