Vanuit mijn perspectief heeft uw server een serieus prestatieprobleem. Zelfs als we aannemen dat geen van de records in de zoekopdracht
select some_col with (nolock) where id_col between 57000000 and 57001000
in het geheugen stond, zou het niet 21 seconden moeten duren om de paar pagina's opeenvolgend van schijf te lezen (je geclusterde index op de id_col zou niet gefragmenteerd moeten zijn als het een automatische identiteit is en je niet iets stoms hebt gedaan zoals het toevoegen van een "desc" naar de indexdefinitie).
Maar als je dat niet kunt/wilt oplossen, zou mijn advies zijn om de update uit te voeren in kleine pakketten, zoals 100-1000 records per keer (afhankelijk van hoeveel tijd de opzoekfunctie in beslag neemt). Eén update/transactie mag niet langer dan 30 seconden duren.
U ziet dat elke update een exclusieve vergrendeling behoudt op alle records die hij heeft gewijzigd totdat de transactie is voltooid. Als u geen expliciete transactie gebruikt, wordt elke instructie uitgevoerd in een enkele, automatische transactiecontext, zodat de vergrendelingen worden vrijgegeven wanneer de update-instructie is voltooid.
Maar je kunt op die manier nog steeds in een impasse raken, afhankelijk van wat de andere processen doen. Als ze ook meer dan één record tegelijk wijzigen, of zelfs als ze leesvergrendelingen op meerdere rijen verzamelen en vasthouden, kunt u deadlocks krijgen.
Om de impasses te vermijden, moet uw update-instructie alle records die het in één keer zal wijzigen, blokkeren. De manier om dit te doen is door de enkele update-instructie (met slechts de paar rijen beperkt door de id_col) in een serialiseerbare transactie te plaatsen zoals
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
Voor elke update is er een update/exclusieve key-range lock op de gegeven records nodig (maar alleen deze, omdat je de update beperkt via de geclusterde indexsleutel). Het wacht tot alle andere updates voor dezelfde records zijn voltooid, krijgt dan zijn vergrendeling (waardoor alle andere transacties worden geblokkeerd, maar nog steeds alleen voor de gegeven records), werkt vervolgens de records bij en laat de vergrendeling los.
De laatste extra instructie is belangrijk, omdat er een sleutelbereikvergrendeling tot "oneindig" nodig is en dus zelfs invoegingen aan het einde van de reeks voorkomen terwijl de update-instructie wordt uitgevoerd.