Моделът е (без обработка на грешки):
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;
Тук не е необходимо да извършвате допълнително четене на таблицата #temp. Вече правите това, като изпробвате актуализацията. За да се предпазите от условия на състезание, вие правите същото, както бихте защитили всеки блок от две или повече изрази, които искате да изолирате:ще го обвиете в транзакция с подходящо ниво на изолация (вероятно да се сериализира тук, макар че всичко това е само има смисъл, когато не говорим за таблица #temp, тъй като тя по дефиниция е сериализирана).
Не сте по-напред, като добавите IF EXISTS
проверете (и ще трябва да добавите заключващи подсказки, за да направите това безопасно / сериализиращо се така или иначе), но може да сте по-назад, в зависимост от това колко пъти актуализирате съществуващи редове спрямо вмъкване на нови. Това може да доведе до много допълнителни I/O.
Хората вероятно ще ви кажат да използвате MERGE
(което всъщност е множество операции зад кулисите и също така трябва да бъде защитено с възможност за сериализиране), призовавам ви да не го правите. Излагам защо тук:
- Внимавайте с изявлението MERGE на SQL Server
За многоредов модел (като TVP) бих се справил с това по същия начин, но няма практичен начин да избегнете второто четене, както можете с едноредовия случай. И не, MERGE
също не го избягва.
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;
Е, предполагам, че има начин да го направя, но не съм го тествал обстойно:
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;
И в двата случая първо извършвате актуализацията, в противен случай ще актуализирате всички редове, които току-що вмъкнахте, което би било разточително.