Tidak akan ada masalah jika variabel tabel hanya memiliki satu nilai. Dengan beberapa baris, ada kemungkinan baru untuk kebuntuan. Misalkan dua proses bersamaan (A & B) dijalankan dengan variabel tabel yang berisi (1, 2) dan (2, 1) untuk perusahaan yang sama.
Proses A membaca tujuan, tidak menemukan baris, dan memasukkan nilai '1'. Ini memegang kunci baris eksklusif pada nilai '1'. Proses B membaca tujuan, tidak menemukan baris, dan memasukkan nilai '2'. Itu memegang kunci baris eksklusif pada nilai '2'.
Sekarang proses A perlu memproses baris 2, dan proses B perlu memproses baris 1. Tidak ada proses yang dapat membuat kemajuan karena membutuhkan kunci yang tidak kompatibel dengan kunci eksklusif yang dipegang oleh proses lain.
Untuk menghindari kebuntuan dengan beberapa baris, baris harus diproses (dan tabel diakses) dalam urutan yang sama setiap waktu . Variabel tabel dalam rencana eksekusi yang ditunjukkan dalam pertanyaan adalah heap, jadi baris tidak memiliki urutan intrinsik (mereka cenderung dibaca dalam urutan penyisipan, meskipun ini tidak dijamin):
Kurangnya urutan pemrosesan baris yang konsisten mengarah langsung ke peluang kebuntuan. Pertimbangan kedua adalah bahwa kurangnya jaminan keunikan kunci berarti bahwa Spool Tabel diperlukan untuk memberikan Perlindungan Halloween yang benar. Spool adalah spool bersemangat, artinya semua baris ditulis ke meja kerja tempdb sebelum dibaca kembali dan diputar ulang untuk operator Insert.
Mendefinisikan ulang TYPE
variabel tabel untuk menyertakan sebuah cluster PRIMARY KEY
:
DROP TYPE dbo.CoUserData;
CREATE TYPE dbo.CoUserData
AS TABLE
(
MyKey integer NOT NULL PRIMARY KEY CLUSTERED,
MyValue integer NOT NULL
);
Rencana pelaksanaan sekarang menunjukkan pemindaian indeks berkerumun dan jaminan keunikan berarti pengoptimal dapat menghapus Spool Tabel dengan aman:
Dalam tes dengan 5000 iterasi MERGE
pernyataan di 128 utas, tidak ada deadlock terjadi dengan variabel tabel berkerumun. Saya harus menekankan bahwa ini hanya berdasarkan pengamatan; variabel tabel berkerumun juga bisa (secara teknis ) menghasilkan baris dalam berbagai pesanan, tetapi kemungkinan pesanan yang konsisten sangat sangat ditingkatkan. Perilaku yang diamati perlu diuji ulang untuk setiap pembaruan kumulatif baru, paket layanan, atau versi baru dari SQL Server, tentu saja.
Dalam hal definisi variabel tabel tidak dapat diubah, ada alternatif lain:
MERGE dbo.CompanyUser AS R
USING
(SELECT DISTINCT MyKey, MyValue FROM @DataTable) AS NewData ON
R.CompanyId = @CompanyID
AND R.UserID = @UserID
AND R.MyKey = NewData.MyKey
WHEN NOT MATCHED THEN
INSERT
(CompanyID, UserID, MyKey, MyValue)
VALUES
(@CompanyID, @UserID, NewData.MyKey, NewData.MyValue)
OPTION (ORDER GROUP);
Ini juga mencapai penghapusan spool (dan konsistensi baris-urutan) dengan biaya memperkenalkan semacam eksplisit:
Rencana ini juga tidak menghasilkan kebuntuan menggunakan tes yang sama. Skrip reproduksi di bawah ini:
CREATE TYPE dbo.CoUserData
AS TABLE
(
MyKey integer NOT NULL /* PRIMARY KEY */,
MyValue integer NOT NULL
);
GO
CREATE TABLE dbo.Company
(
CompanyID integer NOT NULL
CONSTRAINT PK_Company
PRIMARY KEY (CompanyID)
);
GO
CREATE TABLE dbo.CompanyUser
(
CompanyID integer NOT NULL,
UserID integer NOT NULL,
MyKey integer NOT NULL,
MyValue integer NOT NULL
CONSTRAINT PK_CompanyUser
PRIMARY KEY CLUSTERED
(CompanyID, UserID, MyKey),
FOREIGN KEY (CompanyID)
REFERENCES dbo.Company (CompanyID),
);
GO
CREATE NONCLUSTERED INDEX nc1
ON dbo.CompanyUser (CompanyID, UserID);
GO
INSERT dbo.Company (CompanyID) VALUES (1);
GO
DECLARE
@DataTable AS dbo.CoUserData,
@CompanyID integer = 1,
@UserID integer = 1;
INSERT @DataTable
SELECT TOP (10)
V.MyKey,
V.MyValue
FROM
(
VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8),
(9, 9)
) AS V (MyKey, MyValue)
ORDER BY NEWID();
BEGIN TRANSACTION;
-- Test MERGE statement here
ROLLBACK TRANSACTION;