Jag slog ihop följande manus för att bevisa detta trick som jag använde tidigare. Om du använder den måste du modifiera den för att passa dina syften. Kommentarer följer:
/*
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 här är att du kan ställa in värden i lokala variabler i en Update-sats. Ovan ställs "flagga"-värdet bara in om uppdateringen fungerar (det vill säga uppdateringskriterierna är uppfyllda); annars kommer det inte att ändras (här, vänster vid null), du kan kontrollera det och bearbeta därefter.
När det gäller transaktionen och att göra den serialiserbar, skulle jag vilja veta mer om vad som måste kapslas in i transaktionen innan jag föreslår hur man går vidare.
-- Tillägg, uppföljning från den andra kommentaren nedan -----------
Mr. Saffrons idéer är ett grundligt och solidt sätt att implementera denna rutin eftersom dina primära nycklar definieras utanför och skickas in i databasen (dvs. du använder inte identitetskolumner - bra av mig, de är ofta överanvända).
Jag gjorde några fler tester (lägg till en primärnyckelrestriktion på kolumn ContentId, slå in UPDATEN och INFOGA i en transaktion, lägg till den serialiserbara tipsen till uppdateringen) och ja, det borde göra allt du vill. Den misslyckade uppdateringen slår ett intervalllås på den delen av indexet, och det kommer att blockera alla samtidiga försök att infoga det nya värdet i kolumnen. Naturligtvis, om N förfrågningar skickas samtidigt, kommer den "första" att skapa raden, och den kommer omedelbart att uppdateras av den andra, tredje, etc. - om du inte ställer in "låset" någonstans längs linjen. Bra knep!
(Observera att utan indexet i nyckelkolumnen skulle du låsa hela tabellen. Dessutom kan intervalllåset låsa raderna på "vardera sidan" av det nya värdet - eller så kanske de inte gör det, det gjorde jag inte testa den. Det borde inte spela någon roll, eftersom operationens varaktighet bör [?] vara i ensiffriga millisekunder.)