Вставить в select в несколько связанных таблиц, используя 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цикл единственным способом сделать это?

m4rc
источник

Ответы:

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, может ссылаться на другие таблицы, кроме just 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;

полностью исключая необходимость в представлении и триггере, хотя, если вы будете часто выполнять эту операцию в своем коде, триггер view + все равно стоит рассмотреть, чтобы абстрагироваться от необходимости использовать несколько операторов каждый раз.

ПРЕДУПРЕЖДЕНИЕ: все вышеперечисленные 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)? Если нет, возможно, вам следует добавить тот, который вы могли бы использовать для вставки данных в Table1и Table2.

В качестве примера; Я создал образец схемы, вставил в нее тестовые данные DataTable, изменил столбец, затем использовал его для вставки данных в оба и :DataTableIDENTITY(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
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получить идентификатор строки из предыдущей вставки, но для меня это всегда одна строка.

Мухаммед Вакас Азиз
источник