2011-06-27 35 views
1

我需要更新表的所有行的列,我需要使用UPDLOCK来做到这一点。updlock vs更新光标

例如:

UPDATE table (UPDLock) 
    SET column_name = ‘123’ 

另一种替代方法是使用用于更新光标和更新的每一行。第二种方法的好处是锁定不会保持到事务结束,并且同一行的并发更新可能会更早发生。同时更新游标据说性能不佳。哪种方法更好?

编辑:

假设该列与从表中的另一列导出的值进行更新。换句话说,column_name = f(column_name_1)

+2

为什么你需要给锁提示;为什么不让SQL服务器为你选择合适的呢? – vcsjones

+0

与vcsjones达成一致,为什么您认为在这种情况下您需要使用UPDLOCK,即使这个整个表更新真的最终成为业务问题的最佳解决方案? –

+0

你为什么不向你真正的问题寻求帮助? – gbn

回答

2

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

这就是所有的说法,首先更新该列以便每一行具有相同的值有什么意义?我怀疑如果该列中的值对行的其余部分没有影响,它可以存储在别处 - 也许是相关的表或单行表。也许该列中的值应该为NULL(在这种情况下,您可以从另一个表中获取该值),除非它被特定行覆盖。在我看来,比起每次触摸表格中的每一行都有更好的解决方案。

+0

我简化了这个问题。该列需要由同一个表中的另一列进行更新。像column_name = column_name1 –

+1

相同的评论适用。为什么你需要为每一行存储两次相同的值?如果这将在所有或大部分时间都成立,那么只有在实际需要不同时才在第二列中加入一个值。你只是在浪费空间和其他处理。 –

3

你不能给写操作提供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,那么答案很简单,会发生什么:如果有人插入值,整个处理完成后,将发生完全一样的事情。

+0

顺便说一句,我完全同意亚伦,如果你的专栏真的在功能上依赖于另一列,那么你应该使用计算列。 –