Złożyłem razem następujący skrypt, aby udowodnić tę sztuczkę, której używałem w poprzednich latach. Jeśli go używasz, musisz go zmodyfikować, aby odpowiadał Twoim celom. Komentarze następują:
/*
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
Sztuczka polega na tym, że możesz ustawić wartości w zmiennych lokalnych w instrukcji Update. Powyżej wartość „flagi” jest ustawiana tylko wtedy, gdy aktualizacja działa (czyli spełnione są kryteria aktualizacji); w przeciwnym razie nie zostanie zmieniony (tutaj pozostawiony na null), możesz to sprawdzić i odpowiednio przetworzyć.
Jeśli chodzi o transakcję i umożliwienie jej serializacji, chciałbym dowiedzieć się więcej o tym, co musi być zawarte w transakcji, zanim zasugeruję, jak postępować.
-- Uzupełnienie, kontynuacja drugiego komentarza poniżej -----------
Pomysły pana Saffrona są dokładnym i solidnym sposobem realizacji tej procedury, ponieważ klucze podstawowe są definiowane na zewnątrz i przekazywane do bazy danych (tzn. nie używasz kolumn tożsamości – w porządku, są one często nadużywane).
Zrobiłem trochę więcej testów (dodałem ograniczenie klucza podstawowego do kolumny ContentId, zawinąłem UPDATE i INSERT w transakcję, dodałem podpowiedź do serializacji do aktualizacji) i tak, to powinno zrobić wszystko, co chcesz. Nieudana aktualizacja powoduje nałożenie blokady zakresu na tę część indeksu, co zablokuje wszelkie jednoczesne próby wstawienia tej nowej wartości do kolumny. Oczywiście, jeśli N żądań zostanie przesłanych jednocześnie, „pierwszy” utworzy wiersz i zostanie on natychmiast zaktualizowany przez drugi, trzeci itd. – chyba że ustawisz „kłódkę” gdzieś wzdłuż linii. Dobra sztuczka!
(Zauważ, że bez indeksu w kolumnie klucza zablokowałbyś całą tabelę. Ponadto blokada zakresu może zablokować wiersze po „po dowolnej stronie” nowej wartości — a może nie, ja tego nie zrobiłem przetestuj to. Nie powinno mieć znaczenia, ponieważ czas trwania operacji powinien [?] być w jednocyfrowych milisekundach.)