Sẽ không có vấn đề gì nếu biến bảng chỉ giữ một giá trị. Với nhiều hàng, có một khả năng mới cho sự bế tắc. Giả sử hai quy trình đồng thời (A & B) chạy với các biến bảng chứa (1, 2) và (2, 1) cho cùng một công ty.
Quá trình A đọc đích, không tìm thấy hàng và chèn giá trị '1'. Nó giữ một khóa hàng độc quyền trên giá trị '1'. Quá trình B đọc đích, không tìm thấy hàng và chèn giá trị '2'. Nó giữ một khóa hàng độc quyền trên giá trị '2'.
Bây giờ xử lý A cần xử lý hàng 2 và quy trình B cần xử lý hàng 1. Cả hai quá trình đều không thể tiến triển vì nó yêu cầu khóa không tương thích với khóa độc quyền được giữ bởi quy trình khác.
Để tránh các bế tắc có nhiều hàng, các hàng cần được xử lý (và các bảng được truy cập) theo cùng một thứ tự mỗi lần . Biến bảng trong kế hoạch thực hiện được hiển thị trong câu hỏi là một đống, vì vậy các hàng không có thứ tự nội tại (chúng hoàn toàn có thể được đọc theo thứ tự chèn, mặc dù điều này không được đảm bảo):
Việc thiếu thứ tự xử lý hàng nhất quán dẫn trực tiếp đến cơ hội bế tắc. Một xem xét thứ hai là việc thiếu một đảm bảo duy nhất quan trọng có nghĩa là Bộ đệm Bảng là cần thiết để cung cấp Bảo vệ Halloween chính xác. Spool là một spool háo hức, có nghĩa là tất cả các hàng được ghi vào một bàn làm việc tempdb trước khi được đọc lại và phát lại cho toán tử Chèn.
Xác định lại biến TYPE
của bảng để bao gồm một cụm PRIMARY KEY
:
DROP TYPE dbo.CoUserData;
CREATE TYPE dbo.CoUserData
AS TABLE
(
MyKey integer NOT NULL PRIMARY KEY CLUSTERED,
MyValue integer NOT NULL
);
Bây giờ kế hoạch thực hiện cho thấy quét chỉ mục được nhóm và đảm bảo tính duy nhất có nghĩa là trình tối ưu hóa có thể loại bỏ Bảng Spool một cách an toàn:
Trong các thử nghiệm với 5000 lần lặp của MERGE
câu lệnh trên 128 luồng, không có sự tắc nghẽn nào xảy ra với biến bảng được nhóm. Tôi nên nhấn mạnh rằng điều này chỉ dựa trên cơ sở quan sát; biến bảng được phân cụm cũng có thể ( về mặt kỹ thuật ) tạo ra các hàng của nó theo nhiều thứ tự khác nhau, nhưng cơ hội của một đơn hàng nhất quán được tăng cường rất nhiều. Tất nhiên, hành vi được quan sát sẽ cần phải được kiểm tra lại cho mỗi bản cập nhật tích lũy mới, gói dịch vụ hoặc phiên bản mới của SQL Server.
Trong trường hợp định nghĩa biến bảng không thể thay đổi, có một cách khác:
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);
Điều này cũng đạt được việc loại bỏ ống chỉ (và tính nhất quán theo thứ tự hàng) với chi phí giới thiệu một loại rõ ràng:
Kế hoạch này cũng không tạo ra bế tắc khi sử dụng thử nghiệm tương tự. Kịch bản sinh sản dưới đây:
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;