Insérer dans la sélection dans plusieurs tables associées à l'aide de INSERT_IDENTITY


10

D'accord pour préparer le terrain. J'ai trois tables, ( Table1, Table2et DataTable) et je veux insérer Table1et Table2utiliser DataTablecomme source. Donc, pour chaque ligne, DataTableje veux une ligne dans Table1et Table2, et Table2doit avoir l'inséré id(PK) de Table1...

Si je devais faire ça ...

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

J'obtiendrais IDle dernier enregistrement inséré dans Table1.

Un CURSORou une WHILEboucle est-il le seul moyen de le faire?

Réponses:


10

Une solution qui pourrait fonctionner pour vous consiste à utiliser la clause OUTPUT, qui crache toutes les lignes insérées, afin que vous puissiez les réinsérer dans une table différente. Cependant, cela limite les contraintes de clé étrangère sur Table2, si la mémoire est suffisante.

Quoi qu'il en soit, la solution ressemblerait à ceci:

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, contrairement aux autres instructions DML, peut référencer d'autres tables que simplement insertedet deleted, ce qui vous est utile ici.

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


4

Si vous prévoyez de le faire régulièrement (c'est-à-dire qu'il fait partie de la logique d'application et non d'un exercice de transformation de données ponctuel), vous pouvez utiliser une vue sur Table1 et Table2 avec un INSTEAD OF INSERTdéclencheur pour gérer le fractionnement des données (et l'organisation les clés / relations) - alors vous feriez simplement:

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

et le déclencheur pourrait être aussi simple que:

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

en supposant que la vue est quelque chose comme:

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

ou s'il peut y avoir des lignes dans chaque table sans lignes correspondantes dans l'autre:

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;

(bien sûr, quelles lignes sont sorties lorsque vous SELECTde la vue n'a pas d'importance si vous n'en avez pas l'intention SELECTet qu'il n'existe que pour fournir un modèle INSERTdans lequel le déclencheur fera sa magie)

Cela suppose que vous avez l'intention d'utiliser un type UUID pour votre clé primaire dans ce cas - si vous utilisez une clé entière incrémentée automatiquement sur table1, il y a un peu plus de travail à faire. Quelque chose comme ce qui suit pourrait fonctionner:

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

et en fait, cette paire d' INSERTinstructions peut fonctionner directement comme une seule comme ceci (que vous utilisiez un INT IDENTITYou un UNIQUEIDENTIFIER DEFAULT NEWID()type pour la clé):

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;

annulant complètement le besoin de la vue et du déclencheur, bien que s'il s'agit d'une opération que vous effectuerez souvent dans votre code, le déclencheur vue + devrait encore être envisagé de faire abstraction du besoin de plusieurs instructions à chaque fois.

CAVEAT: tout le SQL ci-dessus a été tapé de la pensée et non testé, il aura besoin de travail avant qu'il n'y ait une garantie qu'il fonctionnera comme vous en avez besoin.


3

On dirait que tu veux:

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

Ou peut-être utilisez-vous une seule table, si vous avez toujours une ligne dans chaque table ... avez-vous une bonne raison de les diviser en plusieurs tables?


1
Le système était en place avant que je travaille sur le projet et le SE en charge voulait essayer l'héritage de table, ce qui est bien si vous utilisez Entity Framework et que vous faites des trucs à partir du code car il vous cache tout mais quand vous devez changer à ADO à cause des mauvaises performances, c'est un cauchemar!
m4rc

1

À la lecture de votre question et des commentaires sur les autres réponses, il semble que vous tentiez de résoudre un problème DataTableen le divisant en deux nouveaux tableaux.

Je suppose qu'il DataTablen'a pas déjà un seul champ unique tel qu'un IDENTITY(1,1)? Sinon, vous devriez peut-être en ajouter un que vous pourriez utiliser pour insérer des données dans Table1et Table2.

À titre d'exemple; J'ai créé un exemple de schéma, inséré des données de test dans DataTable, modifié DataTablepour avoir une IDENTITY(1,1)colonne, puis utilisé pour insérer des données dans les deux Table1et 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

Cette chose a fonctionné pour moi, je connais sa réponse très tardive mais peut aider les autres. J'ai utilisé IDENT_CURRENTobtenir l'identité de la ligne de l'insert précédent, mais pour moi, c'est toujours une ligne.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.