web-dev-qa-db-ja.com

INSERT_IDENTITYを使用して、selectに複数の関連テーブルに挿入します

シーンを設定してみましょう。 3つのテーブル(Table1Table2DataTable)があり、DataTableをソースとして使用して、Table1Table2に挿入したいと思います。したがって、DataTableのすべての行について、Table1Table2の行が必要です。Table2には、Table1からid(PK)を挿入する必要があります...

私がこれをするなら...

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

最後に挿入されたレコードのIDTable1に取得します。

CURSORまたはWHILEループがこれを行う唯一の方法ですか?

10
m4rc

mightが機能する解決策は、挿入されたすべての行を出力するOUTPUT句を使用しているため、別のテーブルに再挿入できます。 。ただし、メモリが機能する場合、これによりTable2の外部キー制約に制限が課されます。

とにかく、ソリューションは次のようになります。

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は、他のDMLステートメントとは対照的に、inserteddeleted以外のテーブルを参照できます。これは、ここで役立ちます。

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

10

これが定期的に行う予定の場合(つまり、アプリケーションロジックの一部であり、1回限りのデータ変換演習ではない)、_INSTEAD OF INSERT_トリガーを使用してTable1およびTable2へのビューを使用して分割を管理できます。データ(およびキー/関係の配置)-次に、次のようにします。

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

トリガーは次のように簡単にすることができます。

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

ビューが次のようなものであると仮定します:

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

または、他の行と一致しない行が各テーブルにある可能性がある場合:

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

(もちろん、ビューからSELECTしたときに出力される行は、そこからSELECTするつもりがなく、テンプレートをINSERTに提供するためだけに存在する場合、重要ではありません。トリガーが魔法をかけるために)

これは、この場合に主キーにUUIDタイプを使用することを想定しています。table1で自動的に増分する整数キーを使用している場合は、もう少し作業が必要です。次のようなものが機能する可能性があります。

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

実際には、ペアのINSERTステートメントは、キーに_INT IDENTITY_またはUNIQUEIDENTIFIER DEFAULT NEWID()タイプを使用しているかどうかにかかわらず、1回限りのように直接機能する可能性があります):

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

ビューとトリガーの必要性を完全に否定しますが、これがコード内で頻繁に実行される操作である場合、ビューとトリガーは、毎回複数のステートメントの必要性を抽象化することを検討する価値があります。

警告:上記のSQLはすべて、思考から型付けされており、テストされていません。必要に応じて機能することが保証される前に、機能する必要があります。

4
David Spillett

あなたが望むようです:

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

または、各テーブルに常に行がある場合は、1つのテーブルを使用するだけかもしれません...これらを複数のテーブルに分割する十分な理由はありますか?

3
Aaron Bertrand

質問と他の回答のコメントを読んでいると、DataTableの問題を2つの新しいテーブルに分割することで問題を解決しようとしているようです。

DataTableにはIDENTITY(1,1)のような単一の一意のフィールドがまだないのでしょうか?そうでない場合は、おそらくTable1およびTable2にデータを挿入するために使用できるものを追加する必要があります。

例として、サンプルスキーマを作成し、テストデータをDataTableに挿入し、DataTableを変更してIDENTITY(1,1)列を作成し、それを使用してTable1Table2の両方にデータを挿入しました。

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
Max Vernon