2009-09-26 56 views
7

我想更新表中的單個記錄以反映給定的客戶機會話已在多會話環境中獲取記錄(並且現在擁有該記錄以供進一步更新)。我得到這個至今:原子獨佔SQL記錄更新

create procedure AcquireRow(
    @itemNo int,   -- Item ID to acquire 
    @sessNo int,   -- Session ID 
    @res  char(1) out) -- Result 
as 
begin 
    -- Attempt to acquire the row 
    update Items 
    set 
     State = 'A',  -- 'A'=Acquired 
     SessionID = @sessNo 
    where ItemID = @itemNo 
     and State = 'N'; -- 'N'=Not acquired 

    -- Verify that the session actually acquired the row 
    set @res = 'T';  -- 'T'=Success 
    if @@rowcount = 0 
    set @res = 'F';  -- 'F'=Failure 
end; 

out變量@state設置爲'T'如果過程成功收購該行,否則它設置爲'F'表示失敗。

我的問題:這是保證自動工作,以便只有一個會話成功獲取(更新)該行,如果多個會話同時呼叫AcquireRow()?或者有更好的方法來做到這一點?我需要一個明確的rowlock

修訂:
基於Remus的答案,我將因此重新排列代碼:

set @res = 'F'; 
update ...; 
if @@rowcount > 0 
    set @res = 'T'; 

使用一個output條款或分配結果行的ItemIDupdate內的變量也將是審慎的。

+0

一個非常相似的問題已經被問過這樣,但我不能找到它.... – 2009-09-26 00:30:49

+0

這一個類同,但不是我的初衷:http://stackoverflow.com/questions/574549/高效,交易記錄鎖定 – 2009-09-26 00:39:03

回答

9

@@ROWCOUNT臭名昭着容易出錯。例如,在你的情況。從MSDN

陳述,做一個簡單的 分配始終設置爲1。沒有行被髮送到客戶端 的@@ROWCOUNT 值。這些語句 的例子有:SET @local_variable ...

是正確的,你應該在UPDATE後立即檢查@@ ROWCOUNT。使用ROWLOCK更安全,但不是必需的。即使優化器決定使用頁面鎖,「獲取」語義也是正確的。

我可能會更喜歡另一種方法,即使用OUTPUT條款UPDATE

declare @updated table (ItemId int); 

update Items 
set ... 
output inserted.ItemId 
into @updated (ItemId) 
where ... 

這個方案更防止出錯,也更靈活,因爲它允許未知項目Id的獲取:標識收購放置在@updated表變量中,並可以返回給調用者。

作爲一般說明,對'acquire'使用真實,已提交,更新的問題充滿了問題,因爲您無法知道真正獲取了哪些行以及哪些行被放棄(客戶端斷開連接或崩潰而沒有發佈'acquisition 「)。

+0

謝謝,我不知道'@@ rowcount'的特定隱患。在我的原始代碼的早期版本中,我在'update'之前設置了'@ res',所以它沒有這個問題。 我已經使用'output'或'設置@updateID =項目ID = @ itemNo'爲了捕捉受影響的行的ID也考慮過,但不知道這件事。 感謝您的信息。 – 2009-09-26 02:39:28

3

SQL服務器中的UPDATE語句在數據庫引擎正在讀取需要更新的行時獲取更新鎖,在發生寫入操作時將轉換爲獨佔鎖。

當排它鎖在一行上時,所有其他事務都被阻止讀寫(除非讀事務處於READ UNCOMMITTED隔離,或使用NOLOCK提示)。

所以,是的,因爲它的立場,你的UPDATE語句是一個原子autocommited交易,所以這應該在問候多個會話同時稱這是罰款。如果您出於任何原因將其分成多個報表,則需要確保您在SP中明確創建了一個事務。關於@@ ROWCOUNT和‘收購’的一般用法

Remus的評論是非常穩固的建議雖然。

+0

偉大的更新 - 你有任何引用MSDN或類似的這個? – 2013-07-30 09:44:53