updlock vs更新游标

时间:2011-06-27 22:56:03

标签: sql sql-server tsql

我需要更新表中所有行的列,我需要使用UPDLOCK来执行此操作。

例如:

UPDATE table  (UPDLock)
   SET column_name = ‘123’

另一种方法是使用for更新游标并更新每一行。第二种方法的优点是在事务结束之前不会保持锁定,并且可以更快地发生相同行的并发更新。同时更新游标据说表现不佳。哪种方法更好?

编辑:

假设使用从表中的另一列派生的值更新列。换句话说,column_name = f(column_name_1)

2 个答案:

答案 0 :(得分:3)

您不能对UPDATE语句等写操作提供UPDLOCK提示。它将被忽略,因为所有写入(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),您会看到相同的锁定(物理行上的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

同样,获取的锁将与提示相同或没有提示(行键上的独占锁)。

现在回到你的问题,这整个表更新是否可以批量完成? (因为这基本上就是你所要求的)。是的,如果表有一个主键(准确地说,需要的是批处理的唯一索引,最好是聚集索引以避免引爆点问题)。以下是一个示例:

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会发生什么,那么答案很简单:如果有人在整个处理后插入一个值就会发生完全相同的事情已经完成了。

答案 1 :(得分:2)

就我个人而言,我认为单一的UPDATE会好得多。无论并发活动如何,极少数情况下游标总体上会更好。事实上,唯一想到的是一个非常复杂的运行总计查询 - 我认为我从未见过更好的整体性能来自只读,只有SELECT查询。当然,您有更好的测试方法,这是“更好的方法” - 您可以在您面前拥有硬件,架构,数据和使用模式。您所要做的就是进行一些测试。

所有人都说,首先更新该列的重点是什么,以便每一行都具有相同的值?我怀疑如果该列中的值与行的其余部分无关,则可以将其存储在其他位置 - 可能是相关表或单行表。也许该列中的值应该为NULL(在这种情况下,您从另一个表中获取它),除非它被覆盖特定的行。在我看来,这里有一个更好的解决方案,而不是每次触摸表格中的每一行。