Вставте до вибору в декілька пов'язаних таблиць за допомогою 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

Я отримаю IDостанній вставлений запис у Table1.

Є CURSORчи WHILEцикл - єдиний спосіб зробити це?

Відповіді:


10

Рішенням, яке може працювати для вас, є використання пункту OUTPUT, який виплює всі вставлені рядки, щоб ви могли повторно вставити їх в іншу таблицю. Однак це обмежує обмеження зовнішніх ключів у таблиці2, якщо пам'ять слугує.

У будь-якому випадку рішення виглядатиме приблизно так:

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;

заперечуючи необхідність перегляду та тригера повністю, хоча якщо це операція, яку ви будете виконувати часто у своєму коді, то вигляд тригера все-таки варто розглянути можливість кожного разу абстрагувати необхідність у декількох операторах.

CAVEAT: всі вищевказані 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
Система була створена ще до того, як я працював над проектом, і відповідальний ІП хотів спробувати успадкування таблиці, що добре, якщо ви використовуєте Entity Framework і працюєте з кодом, тому що він приховує все від вас, але коли вам доведеться перейти ADO через низьку продуктивність, це кошмар!
m4rc

1

Прочитавши ваше запитання та коментарі до інших відповідей, здається, ви намагаєтеся вирішити проблему DataTable, розділивши її на дві нові таблиці.

Я припускаю, що DataTableще немає жодного унікального поля, такого як IDENTITY(1,1)? Якщо ні, можливо, вам слід додати той, який ви могли використовувати для вставки даних у Table1та Table2.

Як приклад; Я створив зразкову схему, вставив тестові дані в 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отримання ідентичності рядка з попередньої вставки, але для мене це завжди один рядок.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.