Вы не можете дать подсказку 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)
, вы увидите, что вы получаете точно такие же блокировки (X-блокировка на физическом ряду).Вы можете выполнить тот же эксперимент с B-Tree вместо кучи:
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, тогда ответ очень прост: то же самое произойдет, если кто-то вставит значение после завершения всей обработки.