Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
原子独占SQL记录更新_Sql_Sql Server_Tsql - Fatal编程技术网

原子独占SQL记录更新

原子独占SQL记录更新,sql,sql-server,tsql,Sql,Sql Server,Tsql,我想更新表中的一条记录,以反映给定的客户端会话已经在多会话环境中获取了该记录(现在拥有该记录以便进一步更新)。到目前为止,我已经做到了: create procedure AcquireRow( @itemNo int, -- Item ID to acquire @sessNo int, -- Session ID @res char(1) out) -- Result as begin -- Attempt to acq

我想更新表中的一条记录,以反映给定的客户端会话已经在多会话环境中获取了该记录(现在拥有该记录以便进一步更新)。到目前为止,我已经做到了:

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'
,表示失败

我的问题:这是否保证以原子方式工作,因此如果多个会话同时调用
acquisitorow()
,则只有一个会话成功获取(更新)行?还是有更好的方法?我是否需要显式的
行锁

修订:
根据Remus的回答,我将重新排列代码,如下所示:

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

使用
输出
子句或将结果行的
项目ID
赋值给
更新
中的变量也是谨慎的。

@@ROWCOUNT
非常容易出错。例如,在你的情况下。发件人:

简单的语句 分配始终设置
@@ROWCOUNT
值为1。没有行被发送到服务器 客户这些陈述的例子 是:
SET@local\u变量

要正确,您应该在
更新后立即检查@ROWCOUNT。使用行锁更安全,但不是必需的。即使优化器决定使用页面锁,“acquire”语义也是正确的

我可能更喜欢另一种方法,即使用
UPDATE
OUTPUT
子句:

declare @updated table (ItemId int);

update Items
set ...
output inserted.ItemId
into @updated (ItemId)
where ...
该方案更能防止错误,也更灵活,因为它允许获取未知的ItemId:获取的id放在@updated表变量中,并可以返回给调用者


一般来说,为“acquire”使用real、committed和updates会遇到很多问题,因为您无法知道哪些行是真正获取的,哪些行是刚刚放弃的(客户端断开连接或崩溃,不发布“acquisition”).SQL server中的UPDATE语句在数据库引擎读取需要更新的行时获取更新锁,在写入时将转换为独占锁

当排他锁位于一行上时,所有其他事务都被阻止读取和写入它(除非读取事务处于读取未提交隔离状态,或者使用NOLOCK提示)

因此,是的,就目前而言,您的UPDATE语句是一个原子自动提交事务,因此对于同时调用它的多个会话来说,这应该是很好的。如果出于任何原因要将其分解为多个语句,则需要确保在SP中显式创建事务


Remus关于@ROWCOUNT和“acquire”的一般用法的评论是非常可靠的建议。

有人问了一个非常类似的问题,但我找不到它……这个问题很相似,但不是我想的:谢谢,我不知道
的特殊隐患。@@ROWCOUNT
。在我的原始代码的早期版本中,我在
更新之前设置了
@res
,因此它不会有那个问题。我还考虑过使用
output
set@updateID=ItemID=@itemNo
来捕获受影响行的ID,但对此不确定。谢谢你的信息。很棒的更新-你对此有任何MSDN或类似的参考资料吗?