Masukkan ke dalam pilih ke beberapa tabel terkait menggunakan INSERT_IDENTITY

10

Oke mengatur adegan. Saya punya tiga tabel, ( Table1, Table2dan DataTable) dan saya ingin memasukkan Table1dan Table2menggunakan DataTablesebagai sumber. Jadi untuk setiap baris di DataTableSaya ingin baris Table1dan Table2, dan Table2perlu memiliki dimasukkan id(PK) dari Table1...

Jika saya melakukan ini ...

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

Saya akan IDmemasukkan catatan yang dimasukkan terakhir Table1.

Apakah a CURSORatau WHILEloop satu-satunya cara untuk melakukan ini?

m4rc
sumber

Jawaban:

10

Solusi yang mungkin bekerja untuk Anda adalah menggunakan klausa OUTPUT, yang memuntahkan semua baris yang dimasukkan, sehingga Anda dapat memasukkan kembali mereka ke dalam tabel yang berbeda. Namun, ini membatasi batasan kunci asing pada Table2, jika memori berfungsi.

Bagaimanapun, solusinya akan terlihat seperti ini:

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, berbeda dengan pernyataan DML lainnya, dapat mereferensikan tabel selain dari adil inserteddan deleted, yang berguna bagi Anda di sini.

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

Daniel Hutmacher
sumber
4

Jika ini adalah sesuatu yang Anda rencanakan untuk dilakukan secara teratur (yaitu itu adalah bagian dari logika aplikasi dan bukan latihan transformasi data satu kali) maka Anda dapat menggunakan tampilan pada Table1 dan Table2 dengan INSTEAD OF INSERTpemicu untuk mengelola pemisahan data (dan mengatur kunci / hubungan) - maka Anda cukup melakukan:

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

dan pemicunya bisa sesederhana:

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

dengan asumsi tampilan adalah seperti:

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

atau jika mungkin ada baris di setiap tabel tanpa baris yang cocok di yang lain:

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;

(tentu saja apa baris yang dihasilkan ketika Anda SELECTdari tampilan tidak penting jika Anda tidak berniat SELECTdarinya dan itu hanya ada untuk menyediakan templat ke INSERTdalam sebagai pemicu untuk melakukan keajaibannya)

Ini dengan asumsi bahwa Anda berniat untuk menggunakan jenis UUID untuk kunci utama Anda dalam kasus ini - jika Anda menggunakan kunci integer yang bertambah secara otomatis pada table1, ada sedikit pekerjaan yang harus dilakukan. Sesuatu seperti yang berikut ini mungkin berfungsi:

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

dan pada kenyataannya bahwa sepasang INSERTpernyataan dapat bekerja secara langsung sebagai satu kali seperti itu (apakah Anda menggunakan INT IDENTITYatau UNIQUEIDENTIFIER DEFAULT NEWID()mengetik untuk kunci):

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;

meniadakan perlunya tampilan dan pemicu sepenuhnya, meskipun jika ini adalah operasi Anda akan sering melakukan dalam kode Anda tampilan + pemicu masih layak dipertimbangkan untuk mengabstraksi kebutuhan untuk beberapa pernyataan setiap kali.

CAVEAT: semua SQL di atas telah diketik dari pikiran dan tidak diuji, itu akan perlu bekerja sebelum ada jaminan itu akan berfungsi seperti yang Anda butuhkan.

David Spillett
sumber
3

Sepertinya Anda inginkan:

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

Atau mungkin hanya menggunakan satu tabel, jika Anda akan selalu memiliki baris di setiap tabel ... apakah Anda memiliki alasan yang baik untuk membagi ini menjadi beberapa tabel?

Aaron Bertrand
sumber
1
Sistem sudah ada sebelum saya mengerjakan proyek dan SE yang bertugas ingin mencoba tabel inheritance, yang tidak masalah jika Anda menggunakan Entity Framework dan melakukan hal-hal dari kode karena menyembunyikan semuanya dari Anda tetapi ketika Anda harus beralih ke ADO karena kinerjanya yang buruk, ini adalah mimpi buruk!
m4rc
1

Dari membaca pertanyaan Anda, dan komentar pada jawaban lain, sepertinya Anda mencoba untuk memperbaiki masalah dengan DataTablememecahnya menjadi dua tabel baru.

Saya berasumsi DataTablebelum memiliki satu bidang unik seperti IDENTITY(1,1)? Jika tidak, mungkin Anda harus menambahkan satu yang dapat Anda gunakan untuk memasukkan data ke Table1dan Table2.

Sebagai contoh; Saya telah membuat skema sampel, memasukkan data uji DataTable, dimodifikasi DataTableuntuk memiliki IDENTITY(1,1)kolom, kemudian menggunakannya untuk memasukkan data ke dalam keduanya Table1dan 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
sumber
-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

Hal ini bekerja untuk saya, saya tahu jawabannya sangat terlambat tetapi dapat membantu orang lain. Saya dulu IDENT_CURRENTmendapatkan identitas baris dari insert sebelumnya, tetapi bagi saya itu selalu satu baris.

Muhammad Waqas Aziz
sumber