Ho messo insieme il seguente script per provare questo trucco che ho usato negli anni passati. Se lo usi, dovrai modificarlo per adattarlo ai tuoi scopi. I commenti seguono:
/*
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
Il trucco qui è che puoi impostare valori nelle variabili locali all'interno di un'istruzione Update. Sopra, il valore "flag" viene impostato solo se l'aggiornamento funziona (ovvero, i criteri di aggiornamento sono soddisfatti); in caso contrario, non verrà modificato (qui, lasciato su null), puoi verificarlo ed elaborare di conseguenza.
Per quanto riguarda la transazione e la sua serializzazione, vorrei sapere di più su cosa deve essere incapsulato all'interno della transazione prima di suggerire come procedere.
-- Addenda, follow-up dal secondo commento qui sotto -----------
Le idee del signor Saffron sono un modo completo e solido per implementare questa routine poiché le tue chiavi primarie sono definite all'esterno e passate al database (cioè non stai usando colonne di identità - per me va bene, sono spesso abusate).
Ho eseguito altri test (aggiunto un vincolo di chiave primaria sulla colonna ContentId, avvolgere UPDATE e INSERT in una transazione, aggiungere il suggerimento serializzabile all'aggiornamento) e sì, dovrebbe fare tutto ciò che vuoi. L'aggiornamento non riuscito applica un blocco dell'intervallo su quella parte dell'indice e ciò bloccherà tutti i tentativi simultanei di inserire quel nuovo valore nella colonna. Naturalmente, se N richieste vengono inviate contemporaneamente, la "prima" creerà la riga e verrà immediatamente aggiornata dalla seconda, terza, ecc., a meno che non si imposti il "blocco" da qualche parte lungo la linea. Bel trucco!
(Nota che senza l'indice sulla colonna chiave, bloccheresti l'intera tabella. Inoltre, il blocco dell'intervallo potrebbe bloccare le righe su "entrambi i lati" del nuovo valore, o forse no, non l'ho fatto provalo. Non dovrebbe importare, dal momento che la durata dell'operazione dovrebbe essere [?] in millisecondi a una cifra.)