Inserisci in select in più tabelle correlate usando INSERT_IDENTITY


10

Va bene per impostare la scena. Ho tre tabelle, ( Table1, Table2e DataTable) e voglio inserirle Table1e Table2utilizzarle DataTablecome sorgente. Quindi per ogni riga in DataTablevoglio una riga in Table1e Table2, e Table2ho bisogno di avere inserito id(PK) da Table1...

Se dovessi fare questo ...

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

Vorrei ottenere l' IDultimo disco inserito Table1.

A CURSORo WHILEloop sono gli unici modi per farlo?

Risposte:


10

Una soluzione che potrebbe funzionare per te sta usando la clausola OUTPUT, che sputa tutte le righe inserite, quindi puoi reinserirle in una tabella diversa. Tuttavia, ciò pone limitazioni ai vincoli di chiave esterna su Table2, se la memoria serve.

Ad ogni modo, la soluzione sarebbe simile a questa:

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, al contrario delle altre istruzioni DML, può fare riferimento ad altre tabelle oltre a insertede deleted, il che è utile per te qui.

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


4

Se questo è qualcosa che stai pianificando di fare regolarmente (ovvero fa parte della logica dell'applicazione e non un esercizio di trasformazione dei dati una tantum), puoi utilizzare una vista su Table1 e Table2 con un INSTEAD OF INSERTtrigger per gestire la suddivisione dei dati (e l'organizzazione le chiavi / relazioni) - allora dovresti semplicemente fare:

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

e il trigger potrebbe essere semplice come:

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

supponendo che la vista sia simile a:

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

o se potrebbero esserci righe in ciascuna tabella senza corrispondere alle righe nell'altra:

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;

(ovviamente quali righe vengono emesse quando SELECTdalla vista non è importante se non si intende SELECTda esso ed esiste solo per fornire un modello INSERTin cui il trigger deve fare la sua magia)

Ciò presuppone che tu intenda utilizzare un tipo UUID per la tua chiave primaria in questo caso - se stai usando una chiave intera a incremento automatico sulla tabella1, c'è ancora un po 'di lavoro da fare. Qualcosa di simile al seguente potrebbe funzionare:

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

e in effetti quella coppia di INSERTistruzioni potrebbe funzionare direttamente come una tantum in questo modo (sia che tu stia usando un INT IDENTITYo UNIQUEIDENTIFIER DEFAULT NEWID()tipo per la chiave):

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;

negando la necessità della vista e del trigger completamente, sebbene se questa è un'operazione che eseguirai spesso nel tuo codice, la vista + trigger varrebbe comunque la pena considerare di sottrarre la necessità di più istruzioni ogni volta.

CAVEAT: tutto l'SQL sopra è stato digitato dal pensiero e non testato, avrà bisogno di lavoro prima che ci sia la garanzia che funzionerà di cui hai bisogno.


3

Sembra che tu voglia:

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>;

O forse usa solo una tabella, se hai sempre una riga in ogni tabella ... hai una buona ragione per dividerli in più tabelle?


1
Il sistema era attivo prima che io lavorassi al progetto e il responsabile SE voleva provare l'ereditarietà delle tabelle, il che va bene se stai usando Entity Framework e stai facendo cose dal codice perché nasconde tutto da te ma quando devi cambiare ad ADO a causa delle scarse prestazioni, è un incubo!
m4rc,

1

Dalla lettura della tua domanda e dei commenti sulle altre risposte, sembra che tu stia tentando di risolvere un problema DataTablesuddividendolo in due nuove tabelle.

Presumo DataTablenon abbia già un singolo campo unico come un IDENTITY(1,1)? Altrimenti, forse dovresti aggiungerne uno che potresti usare per inserire dati in Table1e Table2.

A titolo di esempio; Ho creato uno schema di esempio, inserito i dati di test DataTable, modificato DataTableper avere una IDENTITY(1,1)colonna, quindi usato quello per inserire i dati in entrambi Table1e 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;

-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

Questa cosa ha funzionato per me, conosco la sua risposta molto tardi, ma può aiutare gli altri. Ho usato IDENT_CURRENTottenere l'identità della riga dal precedente inserimento, ma per me è sempre una riga.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.