2009-09-26 59 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