SQL Server 2008 (1) - MERGE - Insert und Update nach Wahl

Ich beginne hiermit eine lockere Reihe von Artikeln über Neuerungen in SQL Server 2008. Dabei werde ich nur Features beschreiben, die in der jeweils aktuellen CTP-Version vorhanden sind, danit jeder sie ausprobieren kann.

Erstes Thema ist der neue MERGE-Befehl. Er dient im Kern dazu, INSERT und UPDATE miteinander zu kombinieren. Im einfachsten Fall werden Datensätze aus einer Quelltabelle in einer Zieltabelle aktualisiert, wenn es sie dort schon gibt, sonst werden sie neu eingefügt. Solche Befehle gab es in anderen Datenbanksystemen meist unter dem Namen UPSERT oder REPLACE, mit dem SQL:2003 Standard wurde jedoch MERGE definiert. Und dieser Befehl hat es in sich. Wem SELECT schon immer zu simple Syntax hatte dem bietet sich mit MERGE ein neues Betätigungsfeld.

Zuerst: Wofür braucht man das? Der häufigste Anwendugnsfall ist sicherlich die Aktualisierung von Stammdaten in einem Data Warehouse. Aus dem Produktivsystem läßt man sich z.B. die aktuelle Liste der Produkte geben. Wenn man jetzt diese Liste ins Data Warehouse einspielen will sieht die klassische Logik so aus:

1. Aktualisiere alle Produkte, die es im Data Warehouse schon gibt:

UPDATE Products SET ProductName = Products_Staging.ProductName
    FROM Products_Staging
    WHERE Products.ProductID = Products_Staging.ProductID

2. Füge alle Produkte in das Data Warehouse ein, die es im Produktivsystem, aber nicht im Data Warehouse gibt:

INSERT INTO Products (ProductID, ProductName) SELECT ProductID, ProductName
    FROM Products_Staging
    WHERE Products_Staging.ProductID NOT IN
        (SELECT ProductID From Products)

Diese beiden Anweisungen finden sich in meist komplexerer Form in vienen Data Warehouse Ladeskripten. Das ganze ist ziemlich ineffizient.

Mit MERGE kann man das nun in einem Schritt, un damit deutlich effizienter ausführen:

MERGE INTO Products
    USING Products_Staging
    ON (Products.ProductID = Products_Staging.ProductID)
    WHEN MATCHED THEN
        UPDATE SET Products.ProductName = Products_Staging.ProductName
    WHEN NOT MATCHED THEN
        INSERT (ProductID, ProductName)
            VALUES (Products_Staging.ProductID, Products_Staging.ProductName);

Wie funktioniert das?  Nach MERGE INTO muss erst mal die Zieltabelle angegeben werden. Hinter USING kommt die Quelle. Das kann wie in meinem Beispiel ein einfacher Tabellen- oder Viewname sein, es kann aber auch eine komplexe SELECT-Anweisung sein, die dann mit AS einen Alias bekommt, also:

MERGE INTO Products
    USING (SELECT ProductID, ProductName
        FROM Products_Staging
        WHERE ProductName like '%2007')
    AS PS (ProductID, ProductName)
    ON (Products.ProductID = PS.ProductID)
    WHEN MATCHED THEN
        UPDATE SET Products.ProductName = PS.ProductName
    WHEN NOT MATCHED THEN
        INSERT (ProductID, ProductName)
            VALUES (PS.ProductID, PS.ProductName);

Mit ON wird dann das Joinkriterium definiert, mit dem Quelle und Ziel miteinander gejoint werden.

Nach WHEN MATCHED kommt dann die Update-Klausel, die in der Zieltabelle bereits vorhandene Datensätze aktualisiert. Alternativ kann es auch ein DELETE sein, dann werden alle in Quell- und Zieltabelle vorhandenen Datensätze im Ziel gelöscht

Nach WHEN NOT MATCHED steht immer ein INSERT, das im Ziel nicht vorhandene Datensätze einfügt.

Es kann zusätzlich eine dritte Klausel WHEN SOURCE NOT MATCHED geben, die zutrifft, wenn es den Datensatz im Ziel, aber nicht in der Quelle gibt. Diese können dann aktualisiert oder gelöscht werden.

Eien MERGE-Anweisung ist immer mit einem Semikolon abzuschließen.

Hier ist ein Beispielprojekt, das mit SQL Server 2008 Juni CTP funktioniert.

Dieses Projekt verwendet übrigens auch ein anderes neues SQL Server 2008 Feature: Mehrere INSERTs in einem Befehl:

INSERT INTO Products (ProductID, ProductName)
    VALUES
        (1, 'Office'),
        (2, 'SQL'),
        (3, 'Biztalk')

Gruß,
Steffen