2013-06-18 11 views
13

Ich habe ein T-SQL Skript, das einige Synchronisationslogik mit OUTPUT Klausel in MERGE s und INSERT s implementiert.Mehrere OUTPUT-Klauseln in MERGE/INSERT/DELETE SQL-Befehle?

Jetzt füge ich eine Protokollierungsschicht hinzu und ich möchte eine zweite OUTPUT Klausel hinzufügen, um die Werte in eine Berichtstabelle zu schreiben.

ich eine zweite OUTPUT Klausel meiner MERGE Anweisung hinzufügen:

MERGE TABLE_TARGET AS T 
USING TABLE_SOURCE AS S 
ON (T.Code = S.Code) 
WHEN MATCHED AND T.IsDeleted = 0x0 
    THEN UPDATE SET .... 
WHEN NOT MATCHED BY TARGET 
    THEN INSERT .... 
OUTPUT inserted.SqlId, inserted.IncId 
INTO @sync_table 
OUTPUT $action, inserted.Name, inserted.Code; 

Und das funktioniert, aber solange ich versuchen, das Ziel

INTO @report_table; 

ich die folgende Fehlermeldung hinzufügen vor INTO:

A MERGE statement must be terminated by a semicolon (;) 

ich fou nd a similar question here, aber es hat mir nicht weiter geholfen, weil die Felder, die ich einfügen werde, nicht zwischen zwei Tabellen überlappen und ich will die Arbeitssynchronisierungslogik (wenn möglich) nicht ändern.

UPDATE:

Nach der Antwort von Martin Smith ich eine andere Idee hatte, und wieder schrieb meine Frage, wie folgend:

INSERT INTO @report_table (action, name, code) 
SELECT M.Action, M.Name, M.Code 
FROM 
(
MERGE TABLE_TARGET AS T 
USING TABLE_SOURCE AS S 
ON (T.Code = S.Code) 
WHEN MATCHED AND T.IsDeleted = 0x0 
    THEN UPDATE SET .... 
WHEN NOT MATCHED BY TARGET 
    THEN INSERT .... 
OUTPUT inserted.SqlId, inserted.IncId 
INTO @sync_table 
OUTPUT $action as Action, inserted.Name, inserted.Code 
) M 

Leider hat dieser Ansatz nicht funktionieren, wird die folgende Fehlermeldung Ausgabe zur Laufzeit:

An OUTPUT INTO clause is not allowed in a nested INSERT, UPDATE, DELETE, or MERGE statement. 

Also, es gibt definitiv keine Möglichkeit, mehrerezu habenKlauseln in einer einzelnen DML-Anweisung.

Antwort

13

Nicht möglich. Siehe die grammar.

Der Merge-Anweisung hat

[ <output_clause> ] 

Die eckigen Klammern zeigen sie eine optionale Ausgabe Klausel haben. Die Grammatik für das heißt

<output_clause>::= 
{ 
    [ OUTPUT <dml_select_list> INTO { @table_variable | output_table } 
     [ (column_list) ] ] 
    [ OUTPUT <dml_select_list> ] 
} 

Diese Klausel sowohl eine OUTPUT INTO haben kann und ein OUTPUT aber nicht zwei gleiche.

Wenn mehrere der Grammatik erlaubt wurden würde [ ,...n ]

+0

Ja, ich sah dies auf MSDN, aber ich hoffte, dass es einen Hack geben würde, um diese Einschränkung zu überwinden. Wie auch immer, danke, dass du darauf hingewiesen hast! –

+3

Ich würde hinzufügen, dass Sie müssen die Ausgabe-Into kommen zuerst VOR der Ausgabe (genau wie Ihre Grammatik Klammern zeigen). Ich erwähne das nur für den Fall, dass jemand in der falschen Reihenfolge ist und nicht versteht, warum es nicht für sie funktioniert. – MikeTeeVee

+1

Es ist möglich, 'MERGE' in die gespeicherte Prozedur einzubinden und' INSERT ... EXEC' zu verwenden, um das Ergebnis des zweiten 'OUTPUT' in eine zweite Tabelle einzufügen, wie ich in meiner Antwort gezeigt habe. Es hat seine Schattenseiten, kann aber in bestimmten Fällen nützlich sein. –

2

Martin Smith richtig ist, ist es nicht möglich, zwei OUTPUT INTO Klauseln in einer MERGE Erklärung haben, aber er ist auch richtig, dass es ist möglich, einen haben OUTPUT INTO und eine OUTPUT Klausel. OUTPUT INTO fügt seine Ergebnismenge direkt in die angegebene Tabelle ein und die einfache OUTPUT gibt das Ergebnis an den Aufrufer zurück.

So können Sie die MERGE Anweisung in eine gespeicherte Prozedur umbrechen und dann INSERT ... EXEC verwenden, um Ergebnismenge der einfachen OUTPUT in eine zweite Tabelle einzufügen.

CREATE PROCEDURE [dbo].[TestMerge] 
AS 
BEGIN 
    SET NOCOUNT ON; 
    SET XACT_ABORT ON; 

    MERGE TABLE_TARGET AS T 
    USING TABLE_SOURCE AS S 
    ON (T.Code = S.Code) 
    WHEN MATCHED AND T.IsDeleted = 0x0 
     THEN UPDATE SET .... 
    WHEN NOT MATCHED BY TARGET 
     THEN INSERT .... 
    OUTPUT inserted.SqlId, inserted.IncId 
    INTO sync_table 
    OUTPUT $action AS MergeAction, inserted.Name, inserted.Code; 
END 

Nutzungs

INSERT INTO report_table 
EXEC [dbo].[TestMerge]; 

Diese Zeilen in sync_table und in report_table einfügen wird.

Wenn Sie den Ausführungsplan untersuchen, sehen Sie, dass INSERT ... EXEC eine temporäre Tabelle hinter den Kulissen erstellt (siehe auch The Hidden Costs of INSERT EXEC10 von Adam Machanic).

+1

Die "normale" Möglichkeit, die temporäre temporäre Tabelle zwischen EXEC und INSERT zu vermeiden, besteht darin, den Code in eine Inline-Tabellenwertfunktion zu konvertieren. Leider dürfen diese keine Nebenwirkungen haben (wie ein Insert!). JEDOCH können Sie das umgehen, WENN Sie bereit sind, eine Streaming C# SQL CLR zu codieren! Es ist ein langer Weg, aber machbar! – dsz

0

Es tut uns leid, einen alten Thread wieder auferstehen zu lassen, aber ich bin gerade auf dieses Problem gestoßen und habe eine eher praktische als technische Lösung verwendet, die offensichtlich ist oder auch nicht.

Wie bereits erwähnt, ist MERGE nicht dafür ausgelegt. Die INSERT_INTO ... EXEC-Lösung ist eine gute Problemumgehung, aber die bestimmte gespeicherte Prozedur, an der ich arbeite, ist bereits komplex genug.

Um die Dinge einfach für den nächsten Typ zu halten, der an diesem Code arbeiten muss, habe ich nur zwei MERGE-Anweisungen verwendet ... eine, die das Einfügen und eine die Aktualisierung durchführt. Schließlich gibt es kein Gesetz, das besagt, dass Sie nur einen verwenden müssen. Ich habe der Logging-Tabelle eine "action" -Spalte hinzugefügt, in die ich die MERGE-Anweisung entweder "Insert" oder "Update" einfügen kann, je nachdem was sie macht.

Die Leistung braucht nicht genug Aufmerksamkeit, besonders, da dies kein Benutzerprozess ist.

TIPP: Führen Sie zuerst das Update durch, und fügen Sie das zweite ein. Andernfalls erhalten Sie beim ersten Laden einen Einfüge- und einen Aktualisierungsdatensatz für jede importierte Zeile.