Обновление против обновления курсора - PullRequest
1 голос
/ 28 июня 2011

Мне нужно обновить столбец всех строк таблицы, и мне нужно использовать UPDLOCK, чтобы сделать это.

Например:

UPDATE table  (UPDLock)
   SET column_name = ‘123’

Другой альтернативой является использование курсора для обновления и обновления каждой строки.Преимущество второго подхода заключается в том, что блокировка не удерживается до конца транзакции, и одновременное обновление одних и тех же строк может произойти раньше.В то же время считается, что курсоры обновления имеют плохую производительность.Какой подход лучше?

РЕДАКТИРОВАТЬ:

Предположим, столбец обновляется со значением, полученным из другого столбца в таблице.Другими словами, column_name = f (column_name_1)

Ответы [ 2 ]

3 голосов
/ 28 июня 2011

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

2 голосов
/ 28 июня 2011

Лично я думаю, что единственное ОБНОВЛЕНИЕ будет намного лучше.Очень мало случаев, когда курсор будет лучше в целом, независимо от одновременной активности.Фактически, единственное, что приходит на ум, - это очень сложный бегущий итоговый запрос - я не думаю, что когда-либо видел лучшую общую производительность от курсора, который не только для чтения, только запросы SELECT.Конечно, у вас есть намного лучшие средства тестирования, которые являются «лучшим подходом» - у вас есть ваше оборудование, ваша схема, ваши данные и ваши шаблоны использования прямо перед вами.Все, что вам нужно сделать, это выполнить некоторые тесты.

При этом все сказано, какой смысл в первую очередь обновлять этот столбец, чтобы каждая строка имела одинаковое значение?Я подозреваю, что если значение в этом столбце не имеет отношения к остальной части строки, оно может быть сохранено в другом месте - возможно, в связанной таблице или в таблице из одной строки.Возможно, значение в этом столбце должно быть NULL (в этом случае вы получите его из другой таблицы), если оно не переопределено для конкретной строки.Мне кажется, что здесь есть лучшее решение, чем каждый раз прикасаться к каждой строке таблицы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...