Sql 更新光标的updlock vs
我需要更新表中所有行的列,并且需要使用UPDLOCK来完成 例如: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我个人认为单次更新会更好。很少有情况下,
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的观点,如果你的列在功能上确实依赖于另一列,那么你应该使用计算列。