Mit dem MERGE-Befehl in T-SQL kannst du Daten aus einer Quelle mit einer Zieltabelle abgleichen und passende Zeilen aktualisieren oder neue Zeilen einfügen. Auch das Löschen fehlender Zielzeilen ist möglich. Gerade diese Vielseitigkeit verlangt aber eine saubere Vorbereitung: Eindeutige Schlüssel, eine vollständige Quelle und Tests unter realistischen Bedingungen sind wichtiger als eine möglichst kurze SQL-Anweisung.
Was macht MERGE in SQL Server?
MERGE verknüpft eine Quellmenge mit einer Zieltabelle über eine ON-Bedingung. Danach entscheidet der Befehl für jede passende oder fehlende Zeile, welche Aktion ausgeführt wird:
WHEN MATCHED: Eine Quellzeile passt zu einer Zielzeile; du kannst sie aktualisieren oder löschen.WHEN NOT MATCHED BY TARGET: Die Quellzeile fehlt im Ziel; du kannst sie einfügen.WHEN NOT MATCHED BY SOURCE: Die Zielzeile fehlt in der Quelle; du kannst sie aktualisieren oder löschen.
Die letzte Variante ist nur sinnvoll, wenn die Quelle für den betreffenden Datenbereich vollständig ist. Ein Import mit wenigen neuen Datensätzen ist kein Beleg dafür, dass alle anderen Zielzeilen gelöscht werden sollen.
MERGE-Befehl in T-SQL: Syntax und Schlüssel
MERGE dbo.Ziel AS Ziel
USING dbo.Quelle AS Quelle
ON Ziel.ID = Quelle.ID
WHEN MATCHED THEN
UPDATE SET Ziel.Wert = Quelle.Wert
WHEN NOT MATCHED BY TARGET THEN
INSERT (ID, Wert) VALUES (Quelle.ID, Quelle.Wert)
;
Das Semikolon am Ende ist bei MERGE in SQL Server erforderlich. Die ON-Bedingung sollte ausschließlich die fachlichen Schlüsselspalten für den Abgleich enthalten. Filter wie Ziel.Aktiv = 1 gehören nicht als Zusatz in ON: Sie können Zielzeilen künstlich als „nicht vorhanden“ erscheinen lassen und dadurch unerwartete Aktionen auslösen.
Idealerweise erzwingen Primärschlüssel oder eindeutige Indizes die Eindeutigkeit auf beiden Seiten. Wenn zwei Quellzeilen dieselbe Zielzeile aktualisieren wollen, bricht MERGE mit einem Fehler ab. Wenn zwei Quellzeilen einen im Ziel fehlenden Schlüssel liefern, kann ein eindeutiger Zielschlüssel die doppelte Einfügung verhindern. Mehr zu diesem Thema steht im Beitrag Primary Keys und Indizes in SQL Server.
Ausführbares Beispiel ohne AdventureWorks
Das folgende Beispiel verwendet lokale temporäre Tabellen. Du kannst es in einer SQL-Server-Sitzung ausführen, ohne eine Anwendungstabelle anzulegen oder zu verändern. Es zeigt eine Änderung, eine unveränderte Zeile und einen neuen Datensatz.
DROP TABLE IF EXISTS #ArtikelZiel;
DROP TABLE IF EXISTS #ArtikelQuelle;
CREATE TABLE #ArtikelZiel
(
ArtikelID int NOT NULL PRIMARY KEY,
Bezeichnung nvarchar(100) NOT NULL,
Preis decimal(10,2) NOT NULL
);
CREATE TABLE #ArtikelQuelle
(
ArtikelID int NOT NULL PRIMARY KEY,
Bezeichnung nvarchar(100) NOT NULL,
Preis decimal(10,2) NOT NULL
);
INSERT INTO #ArtikelZiel (ArtikelID, Bezeichnung, Preis)
VALUES (1, N'Tastatur', 29.90),
(2, N'Maus', 19.90),
(3, N'Monitor', 179.00);
INSERT INTO #ArtikelQuelle (ArtikelID, Bezeichnung, Preis)
VALUES (1, N'Tastatur', 34.90),
(2, N'Maus', 19.90),
(4, N'Webcam', 49.90);
SELECT * FROM #ArtikelZiel ORDER BY ArtikelID;
SELECT * FROM #ArtikelQuelle ORDER BY ArtikelID;
DROP TABLE IF EXISTS setzt SQL Server 2016 oder neuer voraus. Bei älteren Versionen kannst du die beiden Zeilen in einem neuen Abfragefenster weglassen.
MERGE mit UPDATE und INSERT testen
Beim MERGE-Befehl in T-SQL lohnt es sich, Änderungen nur dann auszuführen, wenn sich Werte wirklich unterscheiden. Im Beispiel sind die Spalten NOT NULL, daher genügt ein direkter Vergleich. Bei nullable Spalten muss der Vergleich auch NULL korrekt behandeln.
BEGIN TRANSACTION;
MERGE #ArtikelZiel AS Ziel
USING #ArtikelQuelle AS Quelle
ON Ziel.ArtikelID = Quelle.ArtikelID
WHEN MATCHED AND
(Ziel.Bezeichnung <> Quelle.Bezeichnung OR Ziel.Preis <> Quelle.Preis)
THEN UPDATE SET
Bezeichnung = Quelle.Bezeichnung,
Preis = Quelle.Preis
WHEN NOT MATCHED BY TARGET
THEN INSERT (ArtikelID, Bezeichnung, Preis)
VALUES (Quelle.ArtikelID, Quelle.Bezeichnung, Quelle.Preis)
OUTPUT $action AS Aktion,
inserted.ArtikelID AS ArtikelID,
deleted.Preis AS PreisVorher,
inserted.Preis AS PreisNachher
;
SELECT * FROM #ArtikelZiel ORDER BY ArtikelID;
ROLLBACK TRANSACTION;
SELECT * FROM #ArtikelZiel ORDER BY ArtikelID;
Das Ergebnis von OUTPUT sollte ein UPDATE für Artikel 1 und ein INSERT für Artikel 4 zeigen. Artikel 2 bleibt unverändert; Artikel 3 wird nicht gelöscht. Die Reihenfolge der OUTPUT-Zeilen ist nicht garantiert. Nach ROLLBACK enthält die Zieltabelle wieder die ursprünglichen drei Artikel. OUTPUT ist eine Kontrollmöglichkeit, aber kein dauerhaftes Auditprotokoll; bei einem Fehler darfst du ausgegebene Zeilen nicht als bestätigte Änderung interpretieren.
WHEN NOT MATCHED BY SOURCE: Löschen bewusst begrenzen
Der alte Artikel zeigt ein WHEN NOT MATCHED BY SOURCE THEN DELETE auf einer aus AdventureWorks kopierten Kundentabelle, während die Quelle nur wenige Kunden enthält. Das würde nahezu alle anderen Zielkunden entfernen. Verwende diese Klausel nur für eine vollständige Momentaufnahme des abgeglichenen Bereichs und nur, wenn Löschungen fachlich ausdrücklich vorgesehen sind.
Im folgenden Test sind die drei Artikel der Quelle als vollständiger Bestand für diese kleine Beispieltabelle gedacht. Deshalb wäre Artikel 3 ein Löschkandidat. Auch hier nimmt ROLLBACK alles zurück:
BEGIN TRANSACTION;
MERGE #ArtikelZiel AS Ziel
USING #ArtikelQuelle AS Quelle
ON Ziel.ArtikelID = Quelle.ArtikelID
WHEN MATCHED AND
(Ziel.Bezeichnung <> Quelle.Bezeichnung OR Ziel.Preis <> Quelle.Preis)