От моя гледна точка вашият сървър има сериозен проблем с производителността. Дори ако приемем, че нито един от записите в заявката
select some_col with (nolock) where id_col between 57000000 and 57001000
беше в паметта, не би трябвало да отнеме 21 секунди, за да прочете няколкото страници последователно от диска (вашият клъстерен индекс на id_col не трябва да бъде фрагментиран, ако е автоматична идентичност и не сте направили нещо глупаво като добавяне на „desc“ към дефиницията на индекса).
Но ако не можете/няма да поправите това, моят съвет е да направите актуализацията в малки пакети като 100-1000 записа наведнъж (в зависимост от това колко време отнема функцията за търсене). Една актуализация/транзакция не трябва да отнема повече от 30 секунди.
Виждате, че всяка актуализация запазва изключително заключване на всички записи, които е променила, докато транзакцията приключи. Ако не използвате изрична транзакция, всеки оператор се изпълнява в единичен автоматичен контекст на транзакция, така че заключванията се освобождават, когато операторът за актуализиране е готов.
Но все още можете да попаднете в задънени блокировки по този начин, в зависимост от това какво правят другите процеси. Ако те модифицират повече от един запис наведнъж или дори ако съберат и задържат брави за четене на няколко реда, можете да получите задънени блокировки.
За да избегнете задънените блокировки, вашето изявление за актуализиране трябва да заключи всички записи, които ще модифицира наведнъж. Начинът да направите това е да поставите единичния израз за актуализиране (само с няколко реда, ограничени от id_col) в транзакция с възможност за сериализиране като
IF @@TRANCOUNT > 0
-- Error: You are in a transaction context already
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- Insert Loop here to work "x" through the id range
BEGIN TRANSACTION
UPDATE SOMETABLE
SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE [some_col] = 243 AND id_col BETWEEN x AND x+500 -- or whatever keeps the update in the small timerange
COMMIT
-- Next loop
-- Get all new records while you where running the loop. If these are too many you may have to paginate this also:
BEGIN TRANSACTION
UPDATE SOMETABLE
SET [some_col] = dbo.ufn_SomeFunction(CONVERT(NVARCHAR(500), another_column))
WHERE [some_col] = 243 AND id_col >= x
COMMIT
За всяка актуализация това ще отнеме актуализация/изключително заключване на ключов диапазон на дадените записи (но само тях, защото ограничавате актуализацията чрез ключа на клъстерния индекс). Той ще изчака всички други актуализации на същите записи да завършат, след което ще получи заключването си (предизвиквайки блокиране за всички други транзакции, но все още само за дадените записи), след това ще актуализира записите и ще освободи заключването.
Последният допълнителен оператор е важен, защото ще отнеме заключване на ключов диапазон до „безкрайност“ и по този начин ще предотврати дори вмъквания в края на диапазона, докато се изпълнява операторът за актуализиране.