你不能给写操作提供UPDLOCK提示,如UPDATE语句。它将被忽略,因为所有写操作(INSERT/UPDATE/DELETE)都采用同一个锁,所以正在更新的行上有一个排它锁。您可以快速地验证这个自己:
create table heap (a int);
go
insert into heap (a) values (1)
go
begin transaction
update heap
--with (UPDLOCK)
set a=2
select * from sys.dm_tran_locks
rollback
如果删除评论--
的with (UPDLOCK)
你会看到你得到excatly同锁(一个X锁的物理行上)。可以用B树做同样的实验,而不是一个堆的:
create table btree (a int not null identity(1,1) primary key, b int)
go
insert into btree (b) values (1)
go
begin transaction
update btree
--with (UPDLOCK)
set b=2
select * from sys.dm_tran_locks
rollback
同样,获取将与或W/O的提示相同的锁(上行密钥的排他锁)。
现在回到你的问题,这整个表更新可以批量完成吗? (因为这基本上是你问的)。是的,如果表有一个主键(准确地说,需要一个独特的批量索引,最好是聚集索引以避免临界点问题)。下面是一个例子:
create table btree (id int not null identity(1,1) primary key, b int, c int);
go
set nocount on;
insert into btree (b) values (rand()*1000);
go 1000
declare @id int = null, @rc int;
declare @inserted table (id int);
begin transaction;
-- first batch has no WHERE clause
with cte as (
select top(10) id, b, c
from btree
order by id)
update cte
set c = b+1
output INSERTED.id into @inserted (id);
set @rc = @@rowcount;
commit;
select @id = max(id) from @inserted;
delete from @inserted;
raiserror (N'Updated %d rows, up to id %d', 0,0,@rc, @id);
begin transaction;
while (1=1)
begin
-- update the next batch of 10 rows, now it has where clause
with cte as (
select top(10) id, b, c
from btree
where id > @id
order by id)
update cte
set c = b+1
output INSERTED.id into @inserted (id);
set @rc = @@rowcount;
if (0 = @rc)
break;
commit;
begin transaction;
select @id = max(id) from @inserted;
delete from @inserted;
raiserror (N'Updated %d rows, up to id %d', 0,0,@rc, @id);
end
commit
go
如果您的表不具有唯一的聚集索引则变得真正棘手要做到这一点,你需要做的光标有做同样的事情。从逻辑的角度来看,索引并不是必需的,没有它会导致每批进行一次全表扫描,这将会非常糟糕。
如果你想知道,如果有人插入值背后当前@id,那么答案很简单,会发生什么:如果有人插入值,整个处理完成后,将发生完全一样的事情。
为什么你需要给锁提示;为什么不让SQL服务器为你选择合适的呢? – vcsjones
与vcsjones达成一致,为什么您认为在这种情况下您需要使用UPDLOCK,即使这个整个表更新真的最终成为业务问题的最佳解决方案? –
你为什么不向你真正的问题寻求帮助? – gbn