Wzór to (bez obsługi błędów):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE #TProductSales SET StockQty = @StockQty, ETA1 = @ETA1
WHERE ProductID = @ProductID;
IF @@ROWCOUNT = 0
BEGIN
INSERT #TProductSales(ProductID, StockQTY, ETA1)
VALUES(@ProductID, @StockQTY, @ETA1);
END
COMMIT TRANSACTION;
Nie musisz tutaj wykonywać dodatkowego odczytu tabeli #temp. Już to robisz, próbując zaktualizować. Aby chronić się przed warunkami wyścigu, robisz to samo, co zabezpieczasz dowolny blok dwóch lub więcej instrukcji, które chcesz wyizolować:owijasz go w transakcję z odpowiednim poziomem izolacji (prawdopodobnie możliwym do serializacji tutaj, chociaż to wszystko tylko ma sens, gdy nie mówimy o tabeli #temp, ponieważ jest ona z definicji serializowana).
Nie jesteś dalej do przodu, dodając IF EXISTS
sprawdź (i będziesz musiał dodać wskazówki dotyczące blokowania, aby i tak uczynić to bezpiecznym / możliwym do serializacji), ale możesz być dalej w tyle, w zależności od tego, ile razy aktualizujesz istniejące wiersze, a ile razy wstawiasz nowe. To może dać dużo dodatkowych we/wy.
Ludzie prawdopodobnie powiedzą Ci, żebyś używał MERGE
(co w rzeczywistości jest wieloma operacjami za kulisami, a także musi być chronione za pomocą serializacji), zachęcam, aby tego nie robić. Wyjaśniam, dlaczego tutaj:
- Zachowaj ostrożność z instrukcją MERGE SQL Server
W przypadku wzorca wielowierszowego (takiego jak TVP) potraktowałbym to w ten sam sposób, ale nie ma praktycznego sposobu na uniknięcie drugiego odczytu, jak w przypadku jednowierszowego. I nie, MERGE
też tego nie unika.
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE t SET t.col = tvp.col
FROM dbo.TargetTable AS t
INNER JOIN @TVP AS tvp
ON t.ProductID = tvp.ProductID;
INSERT dbo.TargetTable(ProductID, othercols)
SELECT ProductID, othercols
FROM @TVP AS tvp
WHERE NOT EXISTS
(
SELECT 1 FROM dbo.TargetTable
WHERE ProductID = tvp.ProductID
);
COMMIT TRANSACTION;
Cóż, myślę, że jest na to sposób, ale nie przetestowałem tego dokładnie:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
DECLARE @exist TABLE(ProductID int PRIMARY KEY);
UPDATE t SET t.col = tvp.col
OUTPUT deleted.ProductID INTO @exist
FROM dbo.TargetTable AS t
INNER JOIN @tvp AS tvp
ON t.ProductID = tvp.ProductID;
INSERT dbo.TargetTable(ProductID, othercols)
SELECT ProductID, othercols
FROM @tvp AS t
WHERE NOT EXISTS
(
SELECT 1 FROM @exist
WHERE ProductID = t.ProductID
);
COMMIT TRANSACTION;
W obu przypadkach najpierw wykonujesz aktualizację, w przeciwnym razie zaktualizujesz wszystkie wstawione przed chwilą wiersze, co byłoby marnotrawstwem.