Tablo değişkeni yalnızca bir değer elde ederse sorun olmazdı. Birden çok satırda, kilitlenme için yeni bir olasılık var. Aynı şirket için (1, 2) ve (2, 1) içeren tablo değişkenleriyle çalışan iki eşzamanlı işlemin (A ve B) olduğunu varsayalım.
İşlem A, hedefi okur, satır bulamaz ve '1' değerini ekler. '1' değerinde özel bir satır kilidi tutar. B işlemi hedefi okur, hiçbir satır bulamaz ve '2' değerini ekler. '2' değerinde özel bir satır kilidi tutar.
Şimdi A işleminin 2. satırı işlemesi ve B işleminin 1. satırı işlemesi gerekir. İki işlem de diğer işlem tarafından tutulan özel kilitle uyumlu olmayan bir kilit gerektirdiğinden hiçbir işlem ilerleyemez.
Birden çok satırlı kilitlenmeleri önlemek için, satırların her seferinde aynı sırada işlenmesi (ve erişilen tablolara) gerekir . Soruda gösterilen yürütme planındaki tablo değişkeni bir öbektür, bu nedenle satırların kendine özgü bir sırası yoktur (bu garantili olmasa da ekleme sırasına göre okunma olasılığı oldukça yüksektir):
Tutarlı sıra işleme sırasının olmaması doğrudan kilitlenme fırsatına yol açar. İkinci bir husus, anahtarlıkta benzersiz bir güvencenin bulunmamasının, doğru Cadılar Bayramı Koruması sağlamak için bir Tablo Makarası gerektiği anlamına gelmesidir. Makara, istekli bir makaradır, yani tüm satırlar, okuma işleminden önce okunup tekrar oynatılmadan önce bir tempdb çalışma tablasına yazılır .
TYPE
Tablo değişkeninin kümelenmesini içerecek şekilde yeniden tanımlanması PRIMARY KEY
:
DROP TYPE dbo.CoUserData;
CREATE TYPE dbo.CoUserData
AS TABLE
(
MyKey integer NOT NULL PRIMARY KEY CLUSTERED,
MyValue integer NOT NULL
);
Yürütme planı şimdi kümelenmiş dizinin taranmasını gösteriyor ve benzersizlik garantisi, optimize edicinin Tablo Makarasını güvenle kaldırabildiği anlamına geliyor:
MERGE
128 ipliğin ifadesinde 5000 yineleme bulunan testlerde , kümelenmiş tablo değişkeni ile kilitlenme meydana gelmedi. Bunun sadece gözlem temelinde olduğunu vurgulamalıyım; kümelenmiş tablo değişkeni ayrıca ( teknik olarak ) sıralarını çeşitli sıralarda üretebilir, ancak tutarlı bir sıra şansı çok büyük ölçüde artar. Tabii ki, her yeni toplu güncelleme, servis paketi veya yeni SQL Server sürümü için gözlenen davranışın tekrar test edilmesi gerekecektir.
Tablo değişken tanımının değiştirilememesi durumunda, başka bir alternatif daha vardır:
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);
Bu aynı zamanda, açık bir sıralama getirme pahasına makarayı (ve sıra sıra tutarlılığı) ortadan kaldırmayı da başarır:
Bu plan aynı testi kullanarak hiçbir kilitlenme üretmedi. Aşağıdaki üreme komut dosyası:
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;