我想更新表中的单个记录,以反映给定的客户端会话已在多会话环境中获取记录(现在拥有该记录以进行进一步更新)。到目前为止我已经有了这个:
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
子句或将结果行的ItemID
分配给update
中的变量也是谨慎的。
答案 0 :(得分:9)
@@ROWCOUNT
很容易出错。例如,在你的情况下。来自MSDN:
说明简单的陈述 赋值总是设置
@@ROWCOUNT
值为1.没有行发送到 客户。这些陈述的例子 是:SET @local_variable
...
要正确,您应该在UPDATE
之后立即检查@@ ROWCOUNT。使用ROWLOCK更安全,但不是必需的。即使优化器决定使用页锁,'acquire'语义也是正确的。
我可能更喜欢另一种方法,即使用OUTPUT
的{{1}}子句:
UPDATE
这种方案更加防错,也更灵活,因为它允许获取未知的ItemId:获取的id放在@updated表变量中,并可以返回给调用者。
总的来说,对'获取'使用真实的,已提交的更新会遇到问题,因为您无法知道哪些行真正被获取以及哪些行被放弃(客户端断开连接或崩溃而不释放'获取' )。
答案 1 :(得分:3)
SQL Server中的UPDATE语句在数据库引擎读取需要更新的行时获取更新锁,在写入时将其转换为独占锁。
当一行上有一个独占锁时,所有其他事务都被阻止读取和写入(除非读取事务处于READ UNCOMMITTED隔离状态,或者使用了NOLOCK提示)。
所以是的,就目前而言,你的UPDATE语句是一个原子自动交换的事务,因此对于同时调用它的多个会话来说这应该没问题。如果您出于任何原因将其分成多个语句,则需要确保在SP中明确创建了一个事务。
Remus关于@@ ROWCOUNT的评论以及“获得”的一般用法是非常可靠的建议。