Polanya adalah (tanpa penanganan kesalahan):
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;
Anda tidak perlu membaca tabel #temp tambahan di sini. Anda sudah melakukannya dengan mencoba pembaruan. Untuk melindungi dari kondisi balapan, Anda melakukan hal yang sama seperti Anda melindungi blok apa pun dari dua atau lebih pernyataan yang ingin Anda isolasi:Anda akan membungkusnya dalam transaksi dengan tingkat isolasi yang sesuai (mungkin dapat serial di sini, meskipun itu semua hanya masuk akal ketika kita tidak berbicara tentang tabel #temp, karena itu menurut definisi serial).
Anda tidak lebih maju dengan menambahkan IF EXISTS
check (dan Anda perlu menambahkan petunjuk penguncian untuk membuatnya aman/dapat diserialisasi), tetapi Anda dapat tertinggal lebih jauh, tergantung pada berapa kali Anda memperbarui baris yang ada vs. menyisipkan yang baru. Itu bisa menambahkan banyak I/O ekstra.
Orang mungkin akan meminta Anda untuk menggunakan MERGE
(yang sebenarnya adalah beberapa operasi di belakang layar, dan juga perlu dilindungi dengan serializable), saya mendorong Anda untuk tidak melakukannya. Saya jelaskan alasannya di sini:
- Berhati-hatilah dengan Pernyataan MERGE SQL Server
Untuk pola multi-baris (seperti TVP), saya akan menangani ini dengan cara yang hampir sama, tetapi tidak ada cara praktis untuk menghindari pembacaan kedua seperti yang Anda bisa dengan kasus satu baris. Dan tidak, MERGE
tidak menghindarinya juga.
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;
Yah, saya kira ada cara untuk melakukannya, tetapi saya belum mengujinya secara menyeluruh:
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;
Dalam kedua kasus, Anda melakukan pembaruan terlebih dahulu, jika tidak, Anda akan memperbarui semua baris yang baru saja Anda sisipkan, yang akan sia-sia.