2014-02-13 3 views
0

У меня есть данные родительского/дочернего на двух таблицах. Мне нужно скопировать родительские строки обратно в родительскую таблицу, но затем также скопировать дочерние строки в виде дочерних строк созданных новых строк. Я искал этот сайт и Google, но могу найти примеры из Oracle или использовать XML (или иметь много предупреждений о ненадежности), поэтому размещаем здесь полное решение для простого обращения к обратному адресу.Повторяющиеся данные родителя/ребенка

Возьмите следующий код (SqlFiddle):

DECLARE @tbl_person TABLE 
    (
    ID int IDENTITY(1,1), 
    person nvarchar(20) 
    ); 

DECLARE @tbl_drinks TABLE 
    (
    ID int IDENTITY(1,1), 
    personID int, 
    drink nvarchar(20) 
    ); 

DECLARE @i int; 
INSERT INTO @tbl_person (person) VALUES ('Bob'); 
SET @i = SCOPE_IDENTITY(); 
INSERT INTO @tbl_drinks (personID, drink) VALUES (@i, 'Beer'); 
INSERT INTO @tbl_person (person) VALUES ('Wendy'); 
SET @i = SCOPE_IDENTITY(); 
INSERT INTO @tbl_drinks (personID, drink) VALUES (@i, 'Champage'); 
INSERT INTO @tbl_drinks (personID, drink) VALUES (@i, 'Water'); 
INSERT INTO @tbl_person (person) VALUES ('Mike'); 
SET @i = SCOPE_IDENTITY(); 
INSERT INTO @tbl_drinks (personID, drink) VALUES (@i, 'Beer'); 
INSERT INTO @tbl_drinks (personID, drink) VALUES (@i, 'Lemonade'); 

SELECT * FROM @tbl_person; 
SELECT * FROM @tbl_drinks; 

Это производит этот выход:

ID   person 
----------- -------------------- 
1   Bob 
2   Wendy 
3   Mike 

ID   personID drink 
----------- ----------- -------------------- 
1   1   Beer 
2   2   Champage 
3   2   Water 
4   3   Beer 
5   3   Lemonade 

Я знаю, как легко дублировать ни одного человека, плюс свои напитки, но не несколько человек. Предполагая, что мне нужно, чтобы дублировать Боб и Венди мне нужно, чтобы добраться до этого выхода:

ID   person 
----------- -------------------- 
1   Bob 
2   Wendy 
3   Mike 
4   Bob 
5   Wendy 

ID   personID drink 
----------- ----------- -------------------- 
1   1   Beer 
2   2   Champage 
3   2   Water 
4   3   Beer 
5   3   Lemonade 
6   4   Beer 
7   5   Champagne 
8   5   Water 

Я не могу понять, как сравнивать старые и новые столбцы родительского ID для того, чтобы получить дочерние данные.

ответ

1

Проблема в том, что INSERT действительно не имеет «из таблицы», которую вы могли бы ссылаться в пункте OUTPUT. Но вы могли бы добиться того же с MERGE утверждением:

declare @tbl_IDmap table (newID int, oldID int) 

merge @tbl_person as target 
using (
    select ID, person from @tbl_person where ID in (1,2) 
) as source(ID, person) 
on 1=0 
when not matched then 
    insert (person) values(person) 
    output inserted.ID, source.ID into @tbl_IDmap; 

А потом дублировать напитки с новыми идентификаторами:

insert into @tbl_drinks(personID, drink) 
select m.newID, d.drink 
from @tbl_drinks d 
inner join @tbl_IDmap m 
    on m.oldID = d.personID 

Вот ваш SqlFiddle обновляется.

+0

Что такое цель ') в качестве источника (ID, человек)' вместо '), как source'. Я полагаю, что при чтении MSDN это дает имя псевдонима для столбцов в 'source', но в этом случае не понадобится (запрос работает без него)? – EvilDr

+1

Вы правы, в этом случае вам не нужны псевдонимы. – TomT

0

Определено, чтобы добавить дополнительные решения (и подумал об этом большую часть ночи!) Я отправляю дополнительное решение, которое не использует MERGE, надеюсь, что поможет пользователям со старыми версиями SQL. Это более подробно, чем предложение TomT, но работает нормально.

SQLFiddle

-- Gather the people we need to copy 
DECLARE @tbl_IdsToCopy TABLE 
    (
    [counter] int IDENTITY(1,1), 
    [existingId] int 
    ); 
INSERT INTO @tbl_IdsToCopy (existingId) VALUES (1),(2); -- Bob & Wendy 

-- Table to save new person ID's 
DECLARE @tbl_newIds TABLE 
    (
    [counter] int IDENTITY(1,1), 
    [newId] int 
    ); 

-- Create new people and save their new Id's 
INSERT INTO @tbl_person 
    (
    person 
    ) 
OUTPUT 
    INSERTED.ID 
INTO 
    @tbl_newIds 
    (
    [newId] 
    ) 
SELECT 
    p.person 
FROM 
    @tbl_person p INNER JOIN 
    @tbl_IdsToCopy c ON c.existingId = p.ID 
ORDER BY 
    c.[counter]; -- use counter to preserve ordering 

-- map the old ID's to the new ID's and find the drinks for the old ID's 
INSERT INTO @tbl_drinks 
    (
    personID, 
    drink 
    ) 
SELECT 
    n.[newId], 
    d.drink 
FROM 
    @tbl_IdsToCopy c INNER JOIN 
    @tbl_newIds n ON c.[counter] = n.[counter] INNER JOIN -- <-- map the old person ID to the new person Id 
    @tbl_drinks d ON d.personID = c.existingId;    -- <-- find the drinks of the old person Id 

-- Results 
SELECT 
    p.ID, 
    p.person, 
    d.ID, 
    d.drink 
FROM 
    @tbl_person p INNER JOIN 
    @tbl_drinks d ON d.personID = p.ID; 
+1

Аккуратный трюк с '[counter] int IDENTITY (1,1)' – TomT

Смежные вопросы