Jeg slog følgende script sammen for at bevise dette trick, jeg brugte i år tidligere. Hvis du bruger det, skal du ændre det, så det passer til dine formål. Kommentarer følger:
/*
CREATE TABLE Item
(
Title varchar(255) not null
,Teaser varchar(255) not null
,ContentId varchar(30) not null
,RowLocked bit not null
)
UPDATE item
set RowLocked = 1
where ContentId = 'Test01'
*/
DECLARE
@Check varchar(30)
,@pContentID varchar(30)
,@pTitle varchar(255)
,@pTeaser varchar(255)
set @pContentID = 'Test01'
set @pTitle = 'TestingTitle'
set @pTeaser = 'TestingTeasier'
set @check = null
UPDATE dbo.Item
set
@Check = ContentId
,Title = @pTitle
,Teaser = @pTeaser
where ContentID = @pContentID
and RowLocked = 0
print isnull(@check, '<check is null>')
IF @Check is null
INSERT dbo.Item (ContentID, Title, Teaser, RowLocked)
values (@pContentID, @pTitle, @pTeaser, 0)
select * from Item
Tricket her er, at du kan indstille værdier i lokale variabler i en Update-sætning. Ovenfor indstilles "flag"-værdien kun, hvis opdateringen virker (det vil sige, at opdateringskriterierne er opfyldt); ellers bliver det ikke ændret (her venstre ved null), du kan tjekke for det og behandle i overensstemmelse hermed.
Hvad angår transaktionen og gør den serialiserbar, vil jeg gerne vide mere om, hvad der skal indkapsles i transaktionen, før jeg foreslår, hvordan man fortsætter.
-- Tilføjelser, opfølgning fra anden kommentar nedenfor -----------
Mr. Saffrons ideer er en grundig og solid måde at implementere denne rutine på, da dine primære nøgler er defineret udenfor og sendt ind i databasen (dvs. du bruger ikke identitetskolonner - fint af mig, de er ofte overbrugte).
Jeg foretog nogle flere tests (tilføjede en primær nøglebegrænsning på kolonne ContentId, pakke OPDATERING og INDSÆT i en transaktion, føj det serialiserbare hint til opdateringen) og ja, det burde gøre alt, hvad du vil have det til. Den mislykkede opdatering slår en intervallås på den del af indekset, og det vil blokere ethvert samtidig forsøg på at indsætte den nye værdi i kolonnen. Selvfølgelig, hvis N anmodninger indsendes samtidigt, vil den "første" skabe rækken, og den vil straks blive opdateret af den anden, tredje osv. - medmindre du indstiller "låsen" et sted langs linjen. Godt trick!
(Bemærk, at uden indekset på nøglekolonnen, ville du låse hele tabellen. Desuden kan områdelåsen låse rækkerne på "enhver side" af den nye værdi - eller måske vil de ikke, det gjorde jeg ikke test den. Det burde ikke betyde noget, da varigheden af operationen skal [?] være i etcifrede millisekunder.)