Tôi không hoàn toàn chắc chắn, nhưng tôi có cảm tưởng rằng câu hỏi này thực sự là khoảng upsert, đó là hoạt động nguyên tử sau:
- Nếu hàng tồn tại trong cả hai nguồn và đích,
UPDATE
sự Mục tiêu;
- Nếu hàng chỉ tồn tại trong nguồn,
INSERT
hàng vào mục tiêu;
- (Tùy chọn) Nếu hàng tồn tại trong mục tiêu nhưng không phải nguồn,
DELETE
hàng từ mục tiêu.
Developers-quay-DBA thường ngây thơ viết nó chèo-by-hàng, như thế này:
-- For each row in source
IF EXISTS(<target_expression>)
IF @delete_flag = 1
DELETE <target_expression>
ELSE
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
ELSE
INSERT target (<target_columns>)
VALUES (<source_values>)
Đây là chỉ là về điều tồi tệ nhất bạn có thể làm, vì nhiều lý do:
Nó có một điều kiện chủng tộc. Hàng có thể biến mất giữa IF EXISTS
và DELETE
hoặc UPDATE
tiếp theo.
Thật lãng phí. Đối với mỗi giao dịch bạn có một hoạt động bổ sung đang được thực hiện; có lẽ nó tầm thường, nhưng điều đó phụ thuộc hoàn toàn vào mức độ bạn đã lập chỉ mục.
Tồi tệ nhất - nó đang theo mô hình lặp lại, suy nghĩ về những vấn đề này ở cấp độ của một hàng. Điều này sẽ có tác động lớn nhất (tồi tệ nhất) của tất cả về hiệu suất tổng thể.
Tối ưu hóa rất nhỏ (và tôi nhấn mạnh) là chỉ cố gắng UPDATE
; nếu hàng không tồn tại, @@ROWCOUNT
sẽ là 0 và bạn có thể sau đó "một cách an toàn" chèn:
-- For each row in source
BEGIN TRAN
UPDATE target
SET <target_columns> = <source_values>
WHERE <target_expression>
IF (@@ROWCOUNT = 0)
INSERT target (<target_columns>)
VALUES (<source_values>)
COMMIT
trường hợp xấu nhất, điều này sẽ vẫn thực hiện hai hoạt động cho mỗi giao dịch, nhưng ít nhất có một cơ hội của chỉ thực hiện một, và nó cũng loại bỏ các điều kiện chủng tộc (loại).
Nhưng vấn đề thực sự là vấn đề này vẫn đang được thực hiện cho mỗi hàng trong nguồn.
Trước khi SQL Server 2008, bạn phải sử dụng một mô hình 3-giai đoạn khó khăn khi phải đối phó với điều này ở cấp bộ (vẫn còn tốt hơn so với hàng-by-hàng): hiệu suất
BEGIN TRAN
INSERT target (<target_columns>)
SELECT <source_columns> FROM source s
WHERE s.id NOT IN (SELECT id FROM target)
UPDATE t SET <target_columns> = <source_columns>
FROM target t
INNER JOIN source s ON t.d = s.id
DELETE t
FROM target t
WHERE t.id NOT IN (SELECT id FROM source)
COMMIT
Như tôi đã nói, là khá tệ hại về điều này, nhưng vẫn còn tốt hơn rất nhiều so với cách tiếp cận một hàng-tại-một-thời gian.SQL Server 2008, tuy nhiên, cuối cùng đã giới thiệu cú pháp MERGE, vì vậy bây giờ tất cả những gì bạn phải làm là:
MERGE target
USING source ON target.id = source.id
WHEN MATCHED THEN UPDATE <target_columns> = <source_columns>
WHEN NOT MATCHED THEN INSERT (<target_columns>) VALUES (<source_columns>)
WHEN NOT MATCHED BY SOURCE THEN DELETE;
Vậy đó. Một tuyên bố. Nếu bạn đang sử dụng SQL Server 2008 và cần phải thực hiện bất kỳ chuỗi INSERT
, UPDATE
và DELETE
tuỳ thuộc vào việc hay không hàng đã tồn tại - ngay cả khi nó chỉ là một hàng - có không lý do gì không được sử dụng MERGE
.
Bạn thậm chí có thể OUTPUT
các hàng bị ảnh hưởng bởi một MERGE
thành biến bảng nếu bạn cần tìm hiểu sau đó những gì đã được thực hiện. Đơn giản, nhanh chóng và không có rủi ro. Làm đi.
Tại sao không xem xét kế hoạch thực hiện? – RichardOD