แทรกลงในส่วนที่เลือกลงในตารางที่เกี่ยวข้องหลายรายการโดยใช้ INSERT_IDENTITY


10

โอเคตั้งฉากแล้ว ฉันมีสามตาราง ( Table1, Table2และDataTable) และฉันต้องการแทรกลงในTable1และTable2ใช้DataTableเป็นแหล่งที่มา ดังนั้นสำหรับทุกแถวในDataTableฉันต้องการแถวในTable1และTable2และTable2ความต้องการที่จะมีการแทรกid(PK) จากTable1...

ถ้าฉันจะทำสิ่งนี้ ...

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

ฉันได้รับการบันทึกลงในแทรกสุดท้ายIDTable1

a CURSORหรือWHILEloop เป็นวิธีเดียวที่จะทำเช่นนี้?

คำตอบ:


10

วิธีแก้ปัญหาที่อาจใช้ได้ผลกับคุณคือการใช้ส่วนคำสั่ง OUTPUT ซึ่งแยกแถวที่แทรกทั้งหมดออกเพื่อให้คุณสามารถแทรกกลับเข้าไปในตารางอื่นได้ อย่างไรก็ตามสิ่งนี้ทำให้มีข้อ จำกัด เกี่ยวกับข้อ จำกัด foreign key ใน 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 อื่น ๆ สามารถอ้างอิงตารางอื่นที่ไม่ใช่แค่insertedและdeletedซึ่งมีประโยชน์สำหรับคุณที่นี่

เพิ่มเติมได้ที่: http://sqlsunday.com/2013/08/04/cool-merge-features/


4

หากนี่คือสิ่งที่คุณวางแผนที่จะทำอย่างสม่ำเสมอ (เช่นเป็นส่วนหนึ่งของตรรกะแอปพลิเคชันและไม่ใช่แบบฝึกหัดการแปลงข้อมูลแบบครั้งเดียว) จากนั้นคุณสามารถใช้มุมมองไปที่ Table1 และ Table2 พร้อมกับINSTEAD OF INSERTทริกเกอร์เพื่อจัดการแยกข้อมูล กุญแจ / ความสัมพันธ์) จากนั้นคุณจะทำ:

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 สำหรับคีย์หลักของคุณในกรณีนี้ - หากคุณใช้คีย์จำนวนเต็มที่เพิ่มขึ้นโดยอัตโนมัติในตารางที่ 1 จะมีงานอีกเล็กน้อยที่ต้องทำ สิ่งต่อไปนี้อาจใช้งานได้:

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()พิมพ์สำหรับคีย์):

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 ข้างต้นทั้งหมดได้รับการพิมพ์จากความคิดและไม่ได้ทดสอบมันจะต้องทำงานก่อนที่จะมีการรับประกันใด ๆ ว่ามันจะทำงานตามที่คุณต้องการ


3

ดูเหมือนว่าคุณต้องการ:

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
ระบบอยู่ในสถานที่ก่อนที่ฉันจะทำงานในโครงการและผู้ดูแลระบบ SE ต้องการลองใช้การสืบทอดตารางซึ่งเป็นเรื่องที่ดีถ้าคุณใช้ Entity Framework และทำสิ่งต่าง ๆ จากรหัสเพราะซ่อนทุกอย่างจากคุณ แต่เมื่อคุณต้องสลับ ถึง ADO เนื่องจากประสิทธิภาพที่ต่ำมันเป็นฝันร้าย!
m4rc

1

จากการอ่านคำถามของคุณและความคิดเห็นเกี่ยวกับคำตอบอื่น ๆ ดูเหมือนว่าคุณกำลังพยายามแก้ไขปัญหาด้วยการDataTableแยกออกเป็นสองตารางใหม่

ฉันถือว่าDataTableไม่มีฟิลด์ที่ไม่ซ้ำกันเช่นเช่นIDENTITY(1,1)? ถ้าไม่ได้บางทีคุณควรเพิ่มหนึ่งที่คุณสามารถใช้สำหรับการแทรกข้อมูลลงและTable1Table2

โดยวิธีการเป็นตัวอย่าง; ฉันได้สร้าง schema ตัวอย่างแทรกข้อมูลทดสอบลงในDataTableแก้ไขDataTableเพื่อมีIDENTITY(1,1)คอลัมน์จากนั้นใช้เพื่อแทรกข้อมูลลงในทั้งสองTable1และ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

สิ่งนี้เหมาะกับฉันฉันรู้ว่าคำตอบที่ล่าช้ามาก แต่อาจช่วยเหลือผู้อื่นได้ ฉันเคยIDENT_CURRENTได้รับข้อมูลประจำตัวของแถวจากส่วนแทรกก่อนหน้า แต่สำหรับฉันมันเป็นแถวเดียวเสมอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.