Ik heb gewerkt in de veronderstelling dat een enkele instructie in SQL Server consistent is
Die veronderstelling is fout. De volgende twee transacties hebben identieke vergrendelingssemantiek:
STATEMENT
BEGIN TRAN; STATEMENT; COMMIT
Helemaal geen verschil. Enkele verklaringen en automatische toewijzingen veranderen niets.
Dus het samenvoegen van alle logica in één verklaring helpt niet (als dat zo is, was het per ongeluk omdat het plan is gewijzigd).
Laten we het probleem bij de hand oplossen. SERIALIZABLE
lost de inconsistentie op die u ziet, omdat het garandeert dat uw transacties zich gedragen alsof ze single-threaded zijn uitgevoerd. Evenzo gedragen ze zich alsof ze onmiddellijk worden uitgevoerd.
Je krijgt impasses. Als je akkoord gaat met een nieuwe poging, ben je nu klaar.
Als u meer tijd wilt investeren, past u vergrendelingstips toe om exclusieve toegang tot de relevante gegevens te forceren:
UPDATE Gifts -- U-locked anyway
SET GivenAway = 1
WHERE GiftID = (
SELECT TOP 1 GiftID
FROM Gifts WITH (UPDLOCK, HOLDLOCK) --this normally just S-locks.
WHERE g2.GivenAway = 0
AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
ORDER BY g2.GiftValue DESC
)
U ziet nu verminderde gelijktijdigheid. Dat kan helemaal goed zijn, afhankelijk van je belasting.
De aard van uw probleem maakt het moeilijk om gelijktijdigheid te bereiken. Als je daar een oplossing voor nodig hebt, moeten we meer invasieve technieken toepassen.
U kunt de UPDATE een beetje vereenvoudigen:
WITH g AS (
SELECT TOP 1 Gifts.*
FROM Gifts
WHERE g2.GivenAway = 0
AND (SELECT COUNT(*) FROM Gifts g2 WITH (UPDLOCK, HOLDLOCK) WHERE g2.GivenAway = 1) < 5
ORDER BY g2.GiftValue DESC
)
UPDATE g -- U-locked anyway
SET GivenAway = 1
Dit verwijdert één onnodige join.