Зависимые операторы вставки - PullRequest
0 голосов
/ 02 марта 2011

У меня есть таблица с данными о клиенте, Customer (имя, адрес), с такими строками, как «John Doe», «Some Street 123».Для каждой строки в таблице я хочу вставить одну строку в таблицу Person (id, name), а также одну строку в таблице Address (id, person_id, address).два оператора вставки для каждой строки в Customer:

insert into Person(name) values (@name);
insert into Address(person_id, address) values (scope_identity(), @address);

Но это неэффективно.Я хочу сделать вставки в партии, вроде как:

-- This works, the problem is with the Address table...
insert into Person(name)
select name from Customer

-- This looks good but does not work because name is not unique.
insert into Address(person_id, address)
select p.person_id, c.address
from Customer c join Person p on c.name = p.name

Ответы [ 3 ]

1 голос
/ 08 декабря 2011

нет способа сделать это, как вы объясняете, потому что вы потеряли значение scope_identity () для каждой строки первой вставки.

Обходным путем может быть добавление полей первичного ключа Customer в таблицу Person и затем создание соединениявторой вставки с этими полями:

перед вставкой создайте поле customerID для Person

alter table Person add customerID int null;

, затем массовые вставки:

-- inserting customerID
insert into Person(name, customerID)
select name, customerID from Customer

-- joining on customerID.
insert into Address(person_id, address)
select p.person_id, c.address
  from Customer c 
  join Person p on c.customerID = p.customerID

, после чего вы можете удалить поле customerID изПерсональный стол:

alter table Person drop column customerID
1 голос
/ 04 апреля 2019

Оставьте это здесь для попутчика Google, который находит этот пост, как я.

Я нашел это решение, и оно, кажется, работает отлично, и не требует каких-либо изворотливых изменений схемы: https://dba.stackexchange.com/questions/160210/splitting-data-into-two-tables-in-one-go

Они используют оператор MERGE для выполнения начальной вставки в первую таблицу (таблицу, которая генерирует идентификатор, который будет использоваться повсюду). Причина, по которой он использует оператор MERGE, заключается в том, что он позволяет вам использовать оператор OUTPUT, который можно использовать для вывода как нового значения идентификатора, так и значения идентификатора из исходной таблицы (в отличие от использования OUTPUT оператор по стандарту INSERT, который не позволяет выводить идентичность исходных таблиц). Вы можете вставить эти выходные данные в таблицу сопоставления и использовать эту таблицу сопоставления для выполнения второй вставки.

Вот мой пример кода для решения:

------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Set up sample schema and data
------------------------------------------------------------------------------
--Source Data
IF OBJECT_ID('dbo.tmp1') IS NOT NULL DROP TABLE dbo.tmp1 --SELECT * FROM dbo.tmp1
CREATE TABLE dbo.tmp1 (tmp1ID INT IDENTITY(1,1), Col1 CHAR(1) NOT NULL, Col2 CHAR(1) NOT NULL, Col3 CHAR(1) NOT NULL, Col4 CHAR(1) NOT NULL, Col5 CHAR(1) NOT NULL, Col6 CHAR(1) NOT NULL)

INSERT INTO dbo.tmp1 (Col1, Col2, Col3, Col4, Col5, Col6)
SELECT x.c1, x.c2, x.c3, x.c4, x.c5, x.c6
FROM (VALUES ('A','B','C','D','E','F'),
             ('G','H','I','J','K','L'),
             ('M','N','O','P','Q','R')
) x(c1,c2,c3,c4,c5,c6)

IF OBJECT_ID('dbo.tmp3') IS NOT NULL DROP TABLE dbo.tmp3 --SELECT * FROM dbo.tmp3
IF OBJECT_ID('dbo.tmp2') IS NOT NULL DROP TABLE dbo.tmp2 --SELECT * FROM dbo.tmp2

--Taget tables to split into
CREATE TABLE dbo.tmp2 (
      tmp2ID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_tmp2 PRIMARY KEY CLUSTERED (tmp2ID ASC)
    , Col1 CHAR(1) NOT NULL
    , Col2 CHAR(1) NOT NULL
    , Col3 CHAR(1) NOT NULL
)

CREATE TABLE dbo.tmp3 (
      tmp2ID INT NOT NULL
    , Col4 CHAR(1) NOT NULL
    , Col5 CHAR(1) NOT NULL
    , Col6 CHAR(1) NOT NULL
    , CONSTRAINT FK_tmp3_tmp2ID FOREIGN KEY(tmp2ID) REFERENCES dbo.tmp2 (tmp2ID)
)
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Split data into two tables
------------------------------------------------------------------------------
DECLARE @Mapping TABLE (tmp1ID INT NOT NULL, tmp2ID INT NOT NULL);

--Use merge statment to output the source data PK as well as the newly inserted identity to generate a mapping table
MERGE INTO dbo.tmp2 AS tgt
USING dbo.tmp1 AS src ON (1=0)
WHEN NOT MATCHED THEN
    INSERT (    Col1,     Col2,     Col3)
    VALUES (src.Col1, src.Col2, src.Col3)
OUTPUT src.tmp1ID, Inserted.tmp2ID INTO @Mapping (tmp1ID, tmp2ID);

--Use the mapping table to insert the split data into the second table
INSERT INTO dbo.tmp3 (tmp2ID, Col4, Col5, Col6)
SELECT t2.tmp2ID, t1.Col4, t1.Col5, t1.Col6
FROM dbo.tmp2 t2
    JOIN @Mapping m ON m.tmp2ID = t2.tmp2ID
    JOIN dbo.tmp1 t1 ON t1.tmp1ID = m.tmp1ID

SELECT tmp2ID, Col1, Col2, Col3 FROM dbo.tmp2
SELECT tmp2ID, Col4, Col5, Col6 FROM dbo.tmp3
------------------------------------------------------------------------------

------------------------------------------------------------------------------
-- Clean up
------------------------------------------------------------------------------
DROP TABLE dbo.tmp1
DROP TABLE dbo.tmp3
DROP TABLE dbo.tmp2
------------------------------------------------------------------------------

------------------------------------------------------------------------------
GO
0 голосов
/ 08 декабря 2011

Лучше, чтобы вы создали какое-то поле уникальных типов в обеих таблицах, связанных с ними. В противном случае вы хотите присоединиться, так как у вас нет уникального поля для условия

...