Verwenden von Quellspalten in der OUTPUT INTO-Klausel einer INSERT-Anweisung (SQL Server)


14

Ich schreibe eine Batch-Verarbeitungs-Insert-Anweisung und möchte eine temporäre Tabelle verwenden, um die eingefügten IDs zu verfolgen, anstatt die Elemente selbst zu durchlaufen und SCOPE_IDENTITY () für jede eingefügte Zeile aufzurufen.

Die Daten, die eingefügt werden müssen, haben (temporäre) IDs, die sie mit anderen Daten verknüpfen, die ebenfalls in eine andere Tabelle eingefügt werden sollen. Daher benötige ich einen Querverweis auf die tatsächliche ID und die temporäre ID.

Dies ist ein Beispiel für das, was ich bisher habe:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name] )
   OUTPUT Inserted.ID, INS.ID INTO @MyCrossRef
   SELECT [NAME] FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

Das Problem ist, dass ich die OUTPUT INTO-Klausel nicht dazu bringen kann, die ID zu akzeptieren. Ich habe es versucht @MyInsertData.IDund andere Tricks, die die Tabelle mit sich selbst verbinden, aber nichts scheint zu funktionieren.

Antworten:


26

Tatsächlich können Sie dasselbe erreichen, indem Sie Ihre INSERTin a ändern MERGE. Während die MERGEAnweisung eigentlich eine ziemlich nette Möglichkeit ist, "Upsets" in SQL Server auszuführen, hindert Sie nichts daran, sie nur zum Einfügen zu verwenden:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX));

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

MERGE INTO @MyTable AS dest
USING @MyInsertData AS ins ON 1=0   -- always false

WHEN NOT MATCHED BY TARGET          -- happens for every row, because 1 is never 0
    THEN INSERT ([Name])
         VALUES (ins.[NAME])

OUTPUT inserted.ID, ins.ID
INTO @MyCrossRef (NewId, OldId);

-- Check the result
SELECT * FROM @MyCrossRef

Eines der schönen Dinge MERGEist, dass Sie damit auf die Quellenspalten sowie auf die in der Klausel integrierten insertedund deletedTabellen zugreifen können OUTPUT.

Mein Code kann Fehler enthalten, da ich ihn noch nicht getestet habe. Mein Blog-Beitrag von vor einigen Jahren geht etwas detaillierter ein, einschließlich der Abfrageleistung.


Das ist großartig! Dieses Mal muss ich mich an eine Schleife halten, da SQL Server 2005 unterstützt werden muss. Ich werde dies jedoch für zukünftige Projekte berücksichtigen. Vielen Dank!
Louis Somers

3
Genial, dies sollte die akzeptierte Antwort sein.
Chris Peacock

3
Ich wünschte, dies wäre im Stackoverflow statt im DBA-Stack-Austausch. Es hat zu wenig Sichtbarkeit. Erstaunliche Antwort.
Lordbalmon

3
Hat vom ersten Versuch an wie ein Zauber funktioniert ... tolle Antwort!
BeemerGuy

5

Die Ausgabeklausel kann nur auf Daten in den Zielzeilen und Konstanten / Variablen zugreifen, nicht auf Daten von anderen Stellen in der Quelle SELECT, wie wenn Sie in einem Trigger arbeiten.

https://docs.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql besagt:

Alle Verweise auf Spalten in der Tabelle, die geändert werden sollen, müssen mit dem Präfix INSERTED oder DELETED qualifiziert sein.

Um die ursprüngliche ID zu erhalten, müssten Sie sie in die Zieltabelle aufnehmen, damit die Ausgabeklausel sie wie folgt wiedergeben kann:

-- The existing table 
DECLARE @MyTable TABLE (ID INT IDENTITY(1,1), [Name] NVARCHAR(MAX), SourceID INT);

-- My data I want to insert
DECLARE @MyInsertData TABLE (ID INT, [Name] NVARCHAR(MAX));
INSERT INTO @MyInsertData ( ID,Name)
VALUES ( -1 , 'bla'),(-2,'test'),(-3,'last');

DECLARE @MyCrossRef TABLE ([NewId] INT, OldId INT);

INSERT INTO @MyTable ( [Name], SourceID )
   OUTPUT Inserted.ID, Inserted.SourceID INTO @MyCrossRef
   SELECT [NAME], ID FROM @MyInsertData INS

-- Check the result
SELECT * FROM @MyCrossRef

Das Ändern des Schemas des Zielobjekts ist in Ihrer Situation möglicherweise nicht praktikabel, sodass dies möglicherweise nicht anwendbar ist.


3
Das ist eine ziemliche Enttäuschung. Das macht die Ausgabeklausel für mein Szenario unbrauchbar, es sei denn, es gibt eine zweite Spalte, die als Schlüssel verwendet werden kann :-( Na ja, zumindest kann ich aufhören zu versuchen, mit dem Schleifen fortzufahren. Danke.
Louis Somers
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.