Nhiều người sẽ đề nghị bạn sử dụng MERGE
, nhưng tôi cảnh báo bạn chống lại nó. Theo mặc định, nó không bảo vệ bạn khỏi các điều kiện đồng thời và chủng tộc hơn bất kỳ câu lệnh nào và nó đưa ra các mối nguy hiểm khác:
http://www.mssqltips.com/sqlservertip/3074/use-caestion-with-sql-servers-merge-statement/
Ngay cả với cú pháp "đơn giản hơn" này có sẵn, tôi vẫn thích cách tiếp cận này (xử lý lỗi được bỏ qua cho ngắn gọn):
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
UPDATE dbo.table SET ... WHERE PK = @PK;
IF @@ROWCOUNT = 0
BEGIN
INSERT dbo.table(PK, ...) SELECT @PK, ...;
END
COMMIT TRANSACTION;
Rất nhiều người sẽ đề xuất theo cách này:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS (SELECT 1 FROM dbo.table WHERE PK = @PK)
BEGIN
UPDATE ...
END
ELSE
INSERT ...
END
COMMIT TRANSACTION;
Nhưng tất cả những thành tựu này là đảm bảo bạn có thể cần phải đọc bảng hai lần để xác định (các) hàng cần cập nhật. Trong mẫu đầu tiên, bạn sẽ chỉ cần xác định vị trí (các) hàng một lần. (Trong cả hai trường hợp, nếu không tìm thấy hàng nào từ lần đọc đầu tiên, sẽ xảy ra hiện tượng chèn.)
Những người khác sẽ đề xuất theo cách này:
BEGIN TRY
INSERT ...
END TRY
BEGIN CATCH
IF ERROR_NUMBER() = 2627
UPDATE ...
END CATCH
Tuy nhiên, đây là vấn đề nếu không vì lý do nào khác ngoài việc để SQL Server bắt ngoại lệ mà bạn có thể đã ngăn chặn ngay từ đầu đắt hơn nhiều, ngoại trừ trong trường hợp hiếm hoi khi hầu hết mọi thao tác chèn đều thất bại. Tôi chứng minh nhiều như ở đây: