Ur mitt perspektiv har din server ett allvarligt prestandaproblem. Även om vi antar att ingen av posterna i frågan
select some_col with (nolock) where id_col between 57000000 and 57001000
fanns i minnet, borde det inte ta 21 sekunder att läsa de få sidorna sekventiellt från disken (ditt klustrade index på id_col bör inte fragmenteras om det är en auto-identitet och du inte gjorde något dumt som att lägga till en "desc" till indexdefinitionen).
Men om du inte kan/vill fixa det så skulle mitt råd vara att göra uppdateringen i små paket som 100-1000 poster åt gången (beroende på hur mycket tid uppslagsfunktionen tar). En uppdatering/transaktion bör inte ta mer än 30 sekunder.
Du ser att varje uppdatering har ett exklusivt lås på alla poster som den ändrade tills transaktionen är klar. Om du inte använder en explicit transaktion exekveras varje sats i en enda, automatisk transaktionskontext, så låsen släpps när uppdateringssatsen är klar.
Men du kan fortfarande hamna i låsningar på det sättet, beroende på vad de andra processerna gör. Om de ändrar mer än en post åt gången också, eller även om de samlar och håller läslås på flera rader, kan du få dödläge.
För att undvika dödlägen måste din uppdateringssats låsa alla poster som den kommer att ändra på en gång. Sättet att göra detta är att placera den enda uppdateringssatsen (med endast ett fåtal rader begränsade av id_col) i en serialiserbar transaktion som
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
För varje uppdatering kommer detta att kräva en uppdatering/exklusivt nyckelområdeslås på de givna posterna (men bara dem, eftersom du begränsar uppdateringen genom den klustrade indexnyckeln). Den väntar på att alla andra uppdateringar av samma poster ska slutföras och låser sedan (som orsakar blockering för alla andra transaktioner, men fortfarande bara för de givna posterna), uppdaterar sedan posterna och släpper låset.
Den sista extra satsen är viktig, eftersom den kommer att ta ett nyckelområdeslås upp till "oändligt" och därmed förhindra jämna insättningar i slutet av intervallet medan uppdateringssatsen körs.