Mit INSERT_IDENTITY in select in mehrere verwandte Tabellen einfügen

10

Okay, die Szene einstellen. Ich habe drei Tabellen, ( Table1, Table2und DataTable) und ich mag einfügen in Table1und Table2unter Verwendung DataTableals Quelle. Also für jede Zeile in DataTablemöchte ich eine Zeile in Table1und Table2und Table2muss die eingefügte id(PK) von Table1...

Wenn ich das tun würde ...

INSERT INTO Table1 SELECT A, B, C FROM MyTable
INSERT INTO Table2 SELECT IDENTITY_INSERT(), D, E, F FROM MyTable

Ich würde IDden zuletzt eingefügten Datensatz in bekommen Table1.

Ist eine CURSORoder WHILESchleife die einzige Möglichkeit, dies zu tun?

m4rc
quelle

Antworten:

10

Eine Lösung, die für Sie möglicherweise funktioniert, ist die Verwendung der OUTPUT-Klausel, die alle eingefügten Zeilen ausspuckt, sodass Sie sie erneut in eine andere Tabelle einfügen können. Dies schränkt jedoch die Fremdschlüsseleinschränkungen in Tabelle 2 ein, wenn Speicher zur Verfügung steht.

Wie auch immer, die Lösung würde ungefähr so ​​aussehen:

MERGE INTO Table1 AS t1
USING MyTable ON 1=0 -- always generates "not matched by target"

WHEN NOT MATCHED BY TARGET THEN
    -- INSERT into Table1:
    INSERT (A, B, C) VALUES (t1.A, t1.B, t1.C)

--- .. and INSERT into Table2:
OUTPUT inserted.ID, MyTable.D, MyTable.E, MyTable.F
INTO Table2 (ID, D, E, F);

MERGE kann im Gegensatz zu den anderen DML-Anweisungen auf andere Tabellen als nur insertedund verweisen deleted, was für Sie hier nützlich ist.

Mehr: http://sqlsunday.com/2013/08/04/cool-merge-features/

Daniel Hutmacher
quelle
4

Wenn Sie dies regelmäßig planen (dh es ist Teil der Anwendungslogik und keine einmalige Datentransformationsübung), können Sie eine Ansicht auf Tabelle1 und Tabelle2 mit einem INSTEAD OF INSERTAuslöser verwenden, um die Aufteilung der Daten (und die Anordnung) zu verwalten die Schlüssel / Beziehungen) - dann würden Sie einfach tun:

INSERT newView SELECT NEWID(), A, B, C, D, E, F FROM MyTable

und der Auslöser könnte so einfach sein wie:

CREATE trg_newview_insert TRIGGER newView INSTEAD OF UPDATE AS 
    INSERT table1 SELECT ID, A, B, C FROM inserted
    INSERT table2 SELECT ID, D, E, F FROM inserted
GO

Angenommen, die Ansicht ist so etwas wie:

CREATE VIEW newView AS 
SELECT table1.ID, A, B, C, D, E, F 
FROM table1 
    JOIN table2 ON table1.ID = table2.ID;

oder wenn es in jeder Tabelle Zeilen geben könnte, ohne dass die Zeilen in der anderen übereinstimmen:

CREATE VIEW newView AS 
SELECT ISNULL(table1.ID, table2.ID), A, B, C, D, E, F 
FROM table1 
    FULL OUTER JOIN table2 ON table1.ID = table2.ID;

(Natürlich SELECTist es unwichtig, welche Zeilen in der Ansicht ausgegeben werden, wenn Sie dies nicht beabsichtigen, SELECTund es gibt nur eine Vorlage, INSERTin die der Trigger seine Magie ausführen kann.)

Dies setzt voraus, dass Sie in diesem Fall einen UUID-Typ für Ihren Primärschlüssel verwenden möchten. Wenn Sie einen automatisch inkrementierenden Ganzzahlschlüssel in Tabelle 1 verwenden, ist etwas mehr Arbeit zu erledigen. So etwas könnte funktionieren:

CREATE trg_newview_insert TRIGGER newView INSTEAD OF UPDATE AS 
    INSERT table1 (A, B, C) 
    SELECT A, B, C 
    FROM inserted;
    INSERT table2 (ID, D, E, F) 
    SELECT ID, D, E, F 
    FROM table1 AS t 
        JOIN inserted AS i ON t.A = i.A AND t.B = i.B AND t.C = i.C;
GO

und in der Tat könnte dieses INSERTAnweisungspaar direkt als einmalig funktionieren (unabhängig davon, ob Sie einen INT IDENTITYoder einen UNIQUEIDENTIFIER DEFAULT NEWID()Typ für den Schlüssel verwenden):

INSERT table1 (A, B, C) 
SELECT A, B, C 
FROM MyTable;
INSERT table2 (ID, D, E, F) 
SELECT ID, D, E, F 
FROM table1 AS t 
    JOIN MyTable AS i ON t.A = i.A AND t.B = i.B AND t.C = i.C;

Wenn dies eine Operation ist, die Sie häufig in Ihrem Code ausführen, ist es dennoch erwägenswert, die Notwendigkeit mehrerer Anweisungen jedes Mal zu abstrahieren, um die Notwendigkeit mehrerer Anweisungen zu abstrahieren.

CAVEAT: Alle oben genannten SQL-Dateien wurden aus Gedanken eingegeben und nicht getestet. Sie müssen erst bearbeitet werden, bevor garantiert wird, dass sie nach Bedarf funktionieren.

David Spillett
quelle
3

Scheint, als ob Sie wollen:

INSERT dbo.Table1(A,B,C) SELECT A,B,C 
  FROM dbo.DataTable WHERE <identify one row>;

INSERT dbo.Table2(ID,D,E,F) SELECT SCOPE_IDENTITY(),D,E,F
  FROM dbo.DataTable WHERE <identify that same row>;

Oder verwenden Sie einfach eine Tabelle, wenn Sie immer eine Zeile in jeder Tabelle haben möchten. Haben Sie einen guten Grund, diese in mehrere Tabellen aufzuteilen?

Aaron Bertrand
quelle
1
Das System war vorhanden, bevor ich an dem Projekt gearbeitet habe, und die zuständige SE wollte die Tabellenvererbung ausprobieren. Dies ist in Ordnung, wenn Sie Entity Framework verwenden und Dinge aus Code ausführen, da es alles vor Ihnen verbirgt, aber wenn Sie wechseln müssen Für ADO wegen der schlechten Leistung ist es ein Albtraum!
m4rc
1

Nach dem Lesen Ihrer Frage und den Kommentaren zu den anderen Antworten scheint es, als würden Sie versuchen, ein Problem zu beheben, DataTableindem Sie es in zwei neue Tabellen aufteilen.

Ich nehme an, DataTablehat noch kein einziges eindeutiges Feld wie ein IDENTITY(1,1)? Wenn nicht, sollten Sie vielleicht eine hinzufügen, die Sie zum Einfügen von Daten in Table1und verwenden können Table2.

Als Beispiel; Ich habe ein Beispielschema erstellt, Testdaten eingefügt DataTable, DataTablein eine IDENTITY(1,1)Spalte geändert und dieses dann zum Einfügen von Daten in beide Table1und Table2:

USE tempdb;
GO

CREATE TABLE dbo.DataTable
(
    A INT
    , B INT
    , C INT
    , D INT
    , E INT
    , F INT
);

INSERT INTO dbo.DataTable (A, B, C, D, E, F)
VALUES (1, 2, 3, 11, 12, 13)
    , (4, 5, 6, 14, 15, 16)
    , (7, 8, 9, 17, 18, 19);

CREATE TABLE dbo.Table1
(
    Table1PK INT NOT NULL CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , A INT
    , B INT
    , C INT
);

CREATE TABLE dbo.Table2
(
    Table2PK INT NOT NULL CONSTRAINT PK_Table2 PRIMARY KEY CLUSTERED IDENTITY(1,1)
    , Table1PK INT NOT NULL CONSTRAINT FK_Table2_Table1_PK FOREIGN KEY REFERENCES dbo.Table1(Table1PK)
    , D INT
    , E INT
    , F INT
);

ALTER TABLE dbo.DataTable ADD TempCol INT NOT NULL IDENTITY(1,1);

SET IDENTITY_INSERT dbo.Table1 ON;

INSERT INTO Table1 (Table1PK, A, B, C)
SELECT TempCol, A, B, C 
FROM DataTable;

SET IDENTITY_INSERT dbo.Table1 OFF;

INSERT INTO Table2 
SELECT Table1PK, D, E, F 
FROM dbo.DataTable DT
    INNER JOIN dbo.Table1 T ON DT.TempCol = T.Table1PK;

SELECT *
FROM dbo.Table1;

SELECT *
FROM dbo.Table2;
Max Vernon
quelle
-1
INSERT INTO VouchersOtherDetail (
                                 [VouNo],
                                 [Location],
                                 [VouType],
                                 [VouDate],
                                 [InputDate],
                                 [CrossRefGoodsVouNo],
                                 [Reversed],
                                 [ReversalReference],
                                 [UserID]
                                 ) 
SELECT   
                                [VouNo],
                                [Location],
                                [VouType],
                                [VouDate],
                                [InputDate],
                                [CrossRefGoodsVouNo],
                                [Reversed],
                                [ReversalReference],
                                [UserID]
FROM @InsertTableForVoucherDetail           

INSERT INTO VouchersDrCrDetail (
                                [VouID],
                                [AccountCode],
                                [CrossReferAccountCode],
                                [Description],
                                [VouDrAmount],
                                [VouCrAmount],
                                [RunningBalance]
                               )
SELECT  -- IDENT_CURRENT to get the identity of row from previous insert
                                 IDENT_CURRENT('VouchersOtherDetail'), 
                                 [AccountCode],
                                 [CrossReferAccountCode],
                                 [Description],
                                 [VouDrAmount],
                                 [VouCrAmount],
                                 [RunningBalance]
FROM @InsertTableForDrAndCR

Dieses Ding hat bei mir funktioniert, ich kenne seine sehr späte Antwort, kann aber anderen helfen. Ich habe IDENT_CURRENTdie Identität der Zeile aus der vorherigen Einfügung abgerufen, aber für mich ist es immer eine Zeile.

Muhammad Waqas Aziz
quelle