私の見解では、サーバーには深刻なパフォーマンスの問題があります。クエリ内のレコードがないと仮定しても
select some_col with (nolock) where id_col between 57000000 and 57001000
がメモリ内にあった場合、ディスクから数ページを順番に読み取るのに21秒かかることはありません(自動IDであり、「desc」を追加するなどの愚かなことをしなかった場合、id_colのクラスター化されたインデックスは断片化されるべきではありませんインデックス定義へ)。
しかし、それを修正できない/修正できない場合は、一度に100〜1000レコードなどの小さなパッケージで更新を行うことをお勧めします(ルックアップ関数が消費する時間によって異なります)。 1回の更新/トランザクションは30秒以内で完了します。
各更新は、トランザクションが完了するまで、変更したすべてのレコードに対して排他ロックを保持していることがわかります。明示的なトランザクションを使用しない場合、各ステートメントは単一の自動トランザクションコンテキストで実行されるため、更新ステートメントが完了するとロックが解放されます。
ただし、他のプロセスの動作によっては、その方法でデッドロックが発生する可能性があります。一度に複数のレコードを変更する場合、または複数の行で読み取りロックを収集して保持している場合でも、デッドロックが発生する可能性があります。
デッドロックを回避するには、updateステートメントが一度に変更するすべてのレコードをロックする必要があります。これを行う方法は、単一の更新ステートメント(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
更新ごとに、指定されたレコードに対して更新/排他的キー範囲ロックが適用されます(ただし、クラスター化されたインデックスキーを使用して更新を制限するため、これらのレコードのみがロックされます)。同じレコードの他の更新が完了するのを待ってから、ロックを取得し(他のすべてのトランザクションをブロックしますが、指定されたレコードのみをブロックします)、レコードを更新してロックを解除します。
最後の追加のステートメントは重要です。これは、「無限大」までのキー範囲のロックが必要であり、更新ステートメントの実行中に範囲の最後に挿入されることさえ防止するためです。