Sql 更新光标的updlock vs

Sql 更新光标的updlock vs,sql,sql-server,tsql,Sql,Sql Server,Tsql,我需要更新表中所有行的列,并且需要使用UPDLOCK来完成 例如: UPDATE table (UPDLock) SET column_name = ‘123’ 另一种方法是使用for update游标并更新每一行。第二种方法的优点是,锁不会一直保持到事务结束,相同行的并发更新可以更快地发生。同时,据说更新游标的性能很差。哪种方法更好 编辑: 假设该列使用从表中另一列派生的值进行更新。换句话说,column_name=fcolumn_name_1我个人认为单次更新会更好。很少有情况下,

我需要更新表中所有行的列,并且需要使用UPDLOCK来完成

例如:

UPDATE table  (UPDLock)
   SET column_name = ‘123’
另一种方法是使用for update游标并更新每一行。第二种方法的优点是,锁不会一直保持到事务结束,相同行的并发更新可以更快地发生。同时,据说更新游标的性能很差。哪种方法更好

编辑:


假设该列使用从表中另一列派生的值进行更新。换句话说,column_name=fcolumn_name_1

我个人认为单次更新会更好。很少有情况下,不管并发活动如何,游标总体上会更好。事实上,我唯一想到的是一个非常复杂的运行总计查询——我认为我从未见过一个非只读、仅选择查询的游标具有更好的整体性能。当然,您有更好的测试方法,这是一种更好的方法—您的硬件、模式、数据和使用模式就在眼前。你所要做的就是做一些测试


总而言之,更新该列以使每一行具有相同的值的第一点是什么?我怀疑,如果该列中的值与行的其余部分无关,那么它可以存储在其他位置—可能是相关表或单行表。该列中的值可能应该为NULL,在这种情况下,您可以从另一个表中获取该值,除非为特定行重写该值。在我看来,这里似乎有一个比每次触摸表中的每一行更好的解决方案。

我个人认为单次更新会更好。很少有情况下,不管并发活动如何,游标总体上会更好。事实上,我唯一想到的是一个非常复杂的运行总计查询——我认为我从未见过一个非只读、仅选择查询的游标具有更好的整体性能。当然,您有更好的测试方法,这是一种更好的方法—您的硬件、模式、数据和使用模式就在眼前。你所要做的就是做一些测试


总而言之,更新该列以使每一行具有相同的值的第一点是什么?我怀疑,如果该列中的值与行的其余部分无关,那么它可以存储在其他位置—可能是相关表或单行表。该列中的值可能应该为NULL,在这种情况下,您可以从另一个表中获取该值,除非为特定行重写该值。在我看来,这里似乎有一个比每次触摸表中的每一行更好的解决方案。

您不能向写操作(如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后面插入一个值会发生什么,那么答案很简单:如果有人在整个处理完成后插入一个值,则会发生完全相同的情况。

您不能向写入操作(如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
同样,获取的锁将与行键上的独占锁相同或不存在提示

现在回到您的问题,整个表更新可以成批完成吗?因为这基本上就是你要问的。是的,如果表有一个主键,那么需要一个唯一的索引t 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 server为您选择合适的解决方案?同意vcsjones的意见,在这种情况下,即使整个表更新最终真的是解决业务问题的最佳解决方案,您为什么不寻求帮助解决实际问题?为什么需要给出锁提示;为什么不让SQL server为您选择合适的解决方案?同意vcsjones的意见,在这种情况下,即使整个表更新最终真的是解决业务问题的最佳解决方案,您为什么不寻求帮助解决实际问题?我简化了问题。该列需要由同一表中的另一列更新。与column\u name=column\u name1类似,同样的注释也适用。为什么需要为每一行存储两次相同的值?如果这在所有或大部分时间都是真的,那么只在第二列实际需要不同的时候才在第二列中输入一个值。你只是在浪费空间和处理,我简化了问题。该列需要由同一表中的另一列更新。与column\u name=column\u name1类似,同样的注释也适用。为什么需要为每一行存储两次相同的值?如果这在所有或大部分时间都是真的,那么只在第二列实际需要不同的时候才在第二列中输入一个值。另外,我完全同意Aaron的观点,如果你的列在功能上确实依赖于另一列,那么你应该使用计算列。顺便说一句,我完全同意Aaron的观点,如果你的列在功能上确实依赖于另一列,那么你应该使用计算列。