Ich versuche, mit einer MERGE
Anweisung Zeilen aus einer Tabelle einzufügen oder zu löschen, möchte jedoch nur auf eine Teilmenge dieser Zeilen reagieren. Die Dokumentation für MERGE
hat eine ziemlich starke Warnung:
Es ist wichtig, nur die Spalten aus der Zieltabelle anzugeben, die für Abgleichszwecke verwendet werden. Geben Sie also Spalten aus der Zieltabelle an, die mit der entsprechenden Spalte der Quellentabelle verglichen werden. Versuchen Sie nicht, die Abfrageleistung zu verbessern, indem Sie die Zeilen in der Zieltabelle in der ON-Klausel herausfiltern, z. B. durch Angabe von AND NOT target_table.column_x = value. Dies kann zu unerwarteten und falschen Ergebnissen führen.
aber genau das muss ich anscheinend tun, um meine MERGE
Arbeit zu machen .
Die Daten, die ich habe, sind eine standardmäßige Viele-zu-Viele-Verknüpfungstabelle von Elementen mit Kategorien (z. B. welche Elemente in welchen Kategorien enthalten sind).
CategoryId ItemId
========== ======
1 1
1 2
1 3
2 1
2 3
3 5
3 6
4 5
Was ich tun muss, ist effektiv alle Zeilen in einer bestimmten Kategorie durch eine neue Liste von Elementen zu ersetzen. Mein erster Versuch dazu sieht folgendermaßen aus:
MERGE INTO CategoryItem AS TARGET
USING (
SELECT ItemId FROM SomeExternalDataSource WHERE CategoryId = 2
) AS SOURCE
ON SOURCE.ItemId = TARGET.ItemId AND TARGET.CategoryId = 2
WHEN NOT MATCHED BY TARGET THEN
INSERT ( CategoryId, ItemId )
VALUES ( 2, ItemId )
WHEN NOT MATCHED BY SOURCE AND TARGET.CategoryId = 2 THEN
DELETE ;
Dies scheint in meinen Tests zu funktionieren, aber ich tue genau das, wovor MSDN mich ausdrücklich warnt. Das macht mich besorgt, dass ich später auf unerwartete Probleme stoße, aber ich kann keine andere Möglichkeit finden, meine MERGE
Zeilen nur mit dem spezifischen Feldwert ( CategoryId = 2
) zu beeinflussen und Zeilen aus anderen Kategorien zu ignorieren.
Gibt es einen "korrekteren" Weg, um dasselbe Ergebnis zu erzielen? Und vor welchen "unerwarteten oder falschen Ergebnissen" warnt mich MSDN?
quelle
Antworten:
Die
MERGE
Anweisung hat eine komplexe Syntax und eine noch komplexere Implementierung. Grundsätzlich besteht die Idee jedoch darin, zwei Tabellen zu verknüpfen, nach Zeilen zu filtern, die geändert (eingefügt, aktualisiert oder gelöscht) werden müssen, und dann die angeforderten Änderungen vorzunehmen. Angesichts der folgenden Beispieldaten:Ziel
Quelle
Das gewünschte Ergebnis besteht darin, Daten im Ziel durch Daten aus der Quelle zu ersetzen, jedoch nur für
CategoryId = 2
. Nach derMERGE
obigen Beschreibung sollten wir eine Abfrage schreiben, die die Quelle und das Ziel nur mit den Schlüsseln verknüpft und die Zeilen nur in denWHEN
Klauseln filtert :Daraus ergeben sich folgende Ergebnisse:
Der Ausführungsplan lautet:
Beachten Sie, dass beide Tabellen vollständig gescannt werden. Wir könnten dies für ineffizient halten, da nur Zeilen in der
CategoryId = 2
Zieltabelle betroffen sind. Hier kommen die Warnungen in der Onlinedokumentation ins Spiel. Ein fehlgeschlagener Versuch zur Optimierung, um nur die erforderlichen Zeilen im Ziel zu berühren, ist:Die Logik in der
ON
Klausel wird als Teil des Joins angewendet. In diesem Fall kommen die eine vollständige äußere Verknüpfung (siehe diesen Online Eintrag warum). Das Anwenden der Prüfung für Kategorie 2 auf die Zielzeilen als Teil eines Outer Joins führt letztendlich dazu, dass Zeilen mit einem anderen Wert gelöscht werden (da sie nicht mit der Quelle übereinstimmen):Die Hauptursache ist derselbe Grund, warum Prädikate sich in einer Outer Join-
ON
Klausel anders verhalten als in derWHERE
Klausel angegeben. DieMERGE
Syntax (und die Join-Implementierung in Abhängigkeit von den angegebenen Klauseln) erschweren es nur, dies zu erkennen.Die Anleitung in der Onlinedokumentation (erweitert im Eintrag " Optimizing Performance" ) bietet Anleitungen, die sicherstellen, dass die korrekte Semantik mithilfe der
MERGE
Syntax ausgedrückt wird , ohne dass der Benutzer alle Implementierungsdetails verstehen oder die Art und Weise berücksichtigen muss, in der das Optimierungsprogramm möglicherweise eine legitime Neuanordnung vornimmt Dinge aus Gründen der Ausführungseffizienz.Die Dokumentation bietet drei Möglichkeiten zur Implementierung einer frühen Filterung:
Die Angabe einer Filterbedingung in der
WHEN
Klausel garantiert korrekte Ergebnisse, kann jedoch dazu führen, dass mehr Zeilen aus den Quell- und Zieltabellen gelesen und verarbeitet werden, als unbedingt erforderlich sind (wie im ersten Beispiel gezeigt).Das Aktualisieren über eine Ansicht , die die Filterbedingung enthält, garantiert auch korrekte Ergebnisse (da geänderte Zeilen für das Aktualisieren über die Ansicht zugänglich sein müssen), erfordert jedoch eine dedizierte Ansicht, die den ungeraden Bedingungen für das Aktualisieren von Ansichten entspricht.
Die Verwendung eines allgemeinen Tabellenausdrucks birgt ähnliche Risiken wie das Hinzufügen von Prädikaten zur
ON
Klausel, allerdings aus leicht unterschiedlichen Gründen. In vielen Fällen wird es sicher sein, aber es erfordert eine fachmännische Analyse des Ausführungsplans, um dies zu bestätigen (und umfangreiche praktische Tests). Zum Beispiel:Dies führt zu korrekten Ergebnissen (nicht wiederholt) mit einem optimaleren Plan:
Der Plan liest nur Zeilen für Kategorie 2 aus der Zieltabelle. Dies ist möglicherweise eine wichtige Überlegung zur Leistung, wenn die Zieltabelle groß ist, aber es ist allzu einfach, dies mit der
MERGE
Syntax falsch zu verstehen.Manchmal ist es einfacher, die
MERGE
DML-Operationen als separate zu schreiben . Dieser Ansatz kann sogar eine bessere Leistung erbringen als ein einzelnerMERGE
, eine Tatsache, die die Menschen oft überrascht.quelle