MERGE
İfadesi karmaşık bir sözdizimi ve daha da karmaşık bir uygulama vardır, ama aslında fikri iki tabloyu birleştirmek değişti (takılı, güncellenmiş veya silinmiş) gereken satırlara filtre uygulayıp, sonra istenen değişiklikleri gerçekleştirmek için. Aşağıdaki örnek veriler göz önüne alındığında:
DECLARE @CategoryItem AS TABLE
(
CategoryId integer NOT NULL,
ItemId integer NOT NULL,
PRIMARY KEY (CategoryId, ItemId),
UNIQUE (ItemId, CategoryId)
);
DECLARE @DataSource AS TABLE
(
CategoryId integer NOT NULL,
ItemId integer NOT NULL
PRIMARY KEY (CategoryId, ItemId)
);
INSERT @CategoryItem
(CategoryId, ItemId)
VALUES
(1, 1),
(1, 2),
(1, 3),
(2, 1),
(2, 3),
(3, 5),
(3, 6),
(4, 5);
INSERT @DataSource
(CategoryId, ItemId)
VALUES
(2, 2);
Hedef
╔════════════╦════════╗
║ CategoryId ║ ItemId ║
╠════════════╬════════╣
║ 1 ║ 1 ║
║ 2 ║ 1 ║
║ 1 ║ 2 ║
║ 1 ║ 3 ║
║ 2 ║ 3 ║
║ 3 ║ 5 ║
║ 4 ║ 5 ║
║ 3 ║ 6 ║
╚════════════╩════════╝
Kaynak
╔════════════╦════════╗
║ CategoryId ║ ItemId ║
╠════════════╬════════╣
║ 2 ║ 2 ║
╚════════════╩════════╝
İstenilen sonuç, hedefteki verileri kaynaktan gelen verilerle değiştirmek, ancak bunun içindir CategoryId = 2
. MERGE
Yukarıda verilen açıklamanın ardından, kaynağı ve hedefi yalnızca tuşlara birleştiren bir sorgu yazmalı ve satırları yalnızca WHEN
yan tümcelerde filtrelemeliyiz :
MERGE INTO @CategoryItem AS TARGET
USING @DataSource AS SOURCE ON
SOURCE.ItemId = TARGET.ItemId
AND SOURCE.CategoryId = TARGET.CategoryId
WHEN NOT MATCHED BY SOURCE
AND TARGET.CategoryId = 2
THEN DELETE
WHEN NOT MATCHED BY TARGET
AND SOURCE.CategoryId = 2
THEN INSERT (CategoryId, ItemId)
VALUES (CategoryId, ItemId)
OUTPUT
$ACTION,
ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;
Bu, aşağıdaki sonuçları verir:
╔═════════╦════════════╦════════╗
║ $ACTION ║ CategoryId ║ ItemId ║
╠═════════╬════════════╬════════╣
║ DELETE ║ 2 ║ 1 ║
║ INSERT ║ 2 ║ 2 ║
║ DELETE ║ 2 ║ 3 ║
╚═════════╩════════════╩════════╝
╔════════════╦════════╗
║ CategoryId ║ ItemId ║
╠════════════╬════════╣
║ 1 ║ 1 ║
║ 1 ║ 2 ║
║ 1 ║ 3 ║
║ 2 ║ 2 ║
║ 3 ║ 5 ║
║ 3 ║ 6 ║
║ 4 ║ 5 ║
╚════════════╩════════╝
Yürütme planı:
Her iki tablonun da tam olarak tarandığına dikkat edin. Bunun yetersiz olduğunu düşünebiliriz, çünkü yalnızca CategoryId = 2
hedef tabloda etkilenecek satırlar . Bu, Çevrimiçi Kitaplar'daki uyarıların girdiği yerdir. Hedefte yalnızca gerekli satırlara dokunmak için yanlış yönlendirilmiş bir girişimde bulunma:
MERGE INTO @CategoryItem AS TARGET
USING
(
SELECT CategoryId, ItemId
FROM @DataSource AS ds
WHERE CategoryId = 2
) AS SOURCE ON
SOURCE.ItemId = TARGET.ItemId
AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT (CategoryId, ItemId)
VALUES (CategoryId, ItemId)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
OUTPUT
$ACTION,
ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;
Maddede yer alan mantık ON
birleştirme işleminin bir parçası olarak uygulanır. Bu durumda, birleşim tam bir dış birleşimdir (nedense bu Çevrimiçi Kitaplar girişine bakın). Kategori 2 kontrolünü hedef satırlara bir dış birleştirmenin parçası olarak uygulamak, sonuçta farklı bir değerin silindiği satırlara neden olur (çünkü kaynakla eşleşmezler):
╔═════════╦════════════╦════════╗
║ $ACTION ║ CategoryId ║ ItemId ║
╠═════════╬════════════╬════════╣
║ DELETE ║ 1 ║ 1 ║
║ DELETE ║ 1 ║ 2 ║
║ DELETE ║ 1 ║ 3 ║
║ DELETE ║ 2 ║ 1 ║
║ INSERT ║ 2 ║ 2 ║
║ DELETE ║ 2 ║ 3 ║
║ DELETE ║ 3 ║ 5 ║
║ DELETE ║ 3 ║ 6 ║
║ DELETE ║ 4 ║ 5 ║
╚═════════╩════════════╩════════╝
╔════════════╦════════╗
║ CategoryId ║ ItemId ║
╠════════════╬════════╣
║ 2 ║ 2 ║
╚════════════╩════════╝
Kök sebep, aynı nedenin, bir dış birleştirme ON
yan tümcesinde yan tümce belirtilmişse yaptıklarından farklı davranmalarını WHERE
öngörmesidir. MERGE
Sözdizimi (ve belirtilen maddelere bağlı olarak katılmak uygulama) sadece zor bu yüzden olduğunu görmek için yapın.
Çevrimiçi Kitapları rehberlik (genişletilmiş Performansı girişi) kullanılarak ifade edilir doğru semantik sağlayacaktır rehberlik sunmaktadır MERGE
kullanıcı mutlaka iyileştirici meşru yeniden düzenlemek olabilir hangi yollar tüm uygulama ayrıntılarını veya hesabı anlamak zorunda kalmadan, sözdizimi yürütme verimliliği nedeniyle şeyler.
Belgeler, erken filtrelemeyi uygulamak için üç potansiyel yol sunar:
Bir filtre durum belirtme WHEN
madde , doğru sonuçlar garanti, ancak kesin olarak gerekli olandan (birinci örnekte olduğu gibi), daha fazla satır okumak ve kaynak ve hedef tablolardan işlenir anlamına gelebilir.
Filtreleme koşulunu içeren bir görünüm aracılığıyla güncelleme yapmak da doğru sonuçları garanti eder (çünkü değiştirilen satırların görünüm boyunca güncelleme için erişilebilir olması gerekir) ancak bu özel bir görünüm gerektirir ve bu görünümlerin güncellenmesi için tuhaf koşulları izler.
Yaygın bir tablo ifadesi kullanmak , ON
maddeye öngörü ekleyerek benzer riskler taşır , ancak biraz farklı nedenlerle. Çoğu durumda güvenli olacaktır, ancak bunu doğrulamak için yürütme planının uzman analizini gerektirir (ve kapsamlı pratik testler). Örneğin:
WITH TARGET AS
(
SELECT *
FROM @CategoryItem
WHERE CategoryId = 2
)
MERGE INTO TARGET
USING
(
SELECT CategoryId, ItemId
FROM @DataSource
WHERE CategoryId = 2
) AS SOURCE ON
SOURCE.ItemId = TARGET.ItemId
AND SOURCE.CategoryId = TARGET.CategoryId
WHEN NOT MATCHED BY TARGET THEN
INSERT (CategoryId, ItemId)
VALUES (CategoryId, ItemId)
WHEN NOT MATCHED BY SOURCE THEN
DELETE
OUTPUT
$ACTION,
ISNULL(INSERTED.CategoryId, DELETED.CategoryId) AS CategoryId,
ISNULL(INSERTED.ItemId, DELETED.ItemId) AS ItemId
;
Bu, daha optimal bir planla doğru sonuçlar (tekrarlanmayan) üretir:
Plan sadece kategori 2 için hedef tablodan satır okur. Hedef tablo büyükse bu önemli bir performans değerlendirmesi olabilir, ancak MERGE
sözdizimini kullanarak bu yanlış elde etmek çok kolaydır .
Bazen MERGE
ayrı DML işlemlerini yazmak daha kolaydır . Bu yaklaşım, insanları şaşırtan bir gerçek olan tek bir şeyden daha iyi bir performans sergileyebilirMERGE
.
DELETE ci
FROM @CategoryItem AS ci
WHERE ci.CategoryId = 2
AND NOT EXISTS
(
SELECT 1
FROM @DataSource AS ds
WHERE
ds.ItemId = ci.ItemId
AND ds.CategoryId = ci.CategoryId
);
INSERT @CategoryItem
SELECT
ds.CategoryId,
ds.ItemId
FROM @DataSource AS ds
WHERE
ds.CategoryId = 2;