Как написать SQL-запрос для вставки тройного отношения «многие к одному» в отношение «многие ко многим»?

Я следующие две таблицы/столбца:

TableA
id | data

TableB
fkeyA1 | fkeyA2 | fkeyA3 | name

Первые три столбца в TableB — это внешние ключи, указывающие на TableA. Я хотел бы преобразовать TableB в следующие две таблицы:

TableC
id | name

TableD
fkeyC | fkeyA

так что одна строка в TableB становится одной строкой в ​​TableC плюс три строки в TableD. Как мне написать SQL-запрос для этого?

РЕДАКТИРОВАТЬ:

В TableB есть уникальный индекс для (fkeyA1, fkeyA2, fkeyA3), но столбец name не обязательно имеет уникальные значения.


person cm007    schedule 18.11.2011    source источник
comment
Соответствуют ли два пользователя с одним и тем же name из таблицы B одному и тому же идентификатору в таблице C?   -  person Adam Wenger    schedule 19.11.2011


Ответы (2)


Вы можете использовать оператор курсора:

declare @idC table(id int)
declare @fkeyC int, @fkeyA1 int, @fkeyA2 int, @fkeyA3 int, @name varchar(8)
declare CursorB cursor fast_forward for select fkeyA1, fkeyA2, fkeyA3, name from TableB
open CursorB
fetch next from CursorB into @fkeyA1, @fkeyA2, @fkeyA3, @name
while @@fetch_status = 0
begin
    insert into TableC(name) output inserted.id into @idC values (@name)
    set @fkeyC = (select top 1 id from @idC)
    insert into TableD(fkeyC, fkeyA) values (@fkeyC, @fkeyA1), (@fkeyC, @fkeyA2), (@fkeyC, @fkeyA3)
    delete from @idC
    fetch next from CursorB into @fkeyA1, @fkeyA2, @fkeyA3, @name
end
close CursorB
deallocate CursorB

Это может быть не лучшим с точки зрения производительности, но позволит использовать неуникальные значения для столбца имени.

person cubetwo1729    schedule 21.11.2011

Я бы начал с вставки в таблицу C, где Id был столбцом IDENTITY.

INSERT INTO TableC(name)
SELECT b.name
FROM TableB AS b

Затем я бы написал вставку:

SELECT c.Id, b.fkeyA1
FROM TableC AS c
INNER JOIN tableB AS b ON c.name = b.name
UNION    
SELECT c.Id, b.fkeyA2
FROM TableC AS c
INNER JOIN tableB AS b ON c.name = b.name
UNION
SELECT c.Id, b.fkeyA3
FROM TableC AS c
INNER JOIN tableB AS b ON c.name = b.name

Другой подход для INSERT в TableD:

SELECT c.Id, a.Id
FROM TableB AS b
INNER JOIN TableC AS c ON b.name = c.name
LEFT JOIN TableA AS a ON b.fkeyA1 = a.Id
   OR b.fkeyA2 = a.Id
   OR b.fkeyA3 = a.Id

Если таблица B дважды содержит имя «Адам», на обе записи из B будет ссылаться один и тот же идентификатор в таблице C, а в таблице D будет 6 записей для этого идентификатора.

Eg.

TableA         TableB                   TableC          TableD
1, 'Data1'     1, 2, 3, 'Adam'          1, 'Adam'       1, 1
2, 'Data2'     4, 5, 6, 'Someone'       2, 'Someone'    1, 2
3, 'Data3'     7, 8, 9, 'Adam'                          1, 3
4, 'Data4'                                              1, 7
5, 'Data5'                                              1, 8
6, 'Data6'                                              1, 9
7, 'Data7'                                              2, 4
8, 'Data8'                                              2, 5
9, 'Data9'                                              2, 6
person Adam Wenger    schedule 18.11.2011
comment
Логически, я не думаю, что вам нужно вкладывать COALESCE() - И в любом случае, я думаю, вы имеете в виду LEFT JOIN TableA as a ON b.fkeyA1 = a.Id OR b.fkeyA2 = a.Id OR b.fkeyA3 = a.Id, ваша текущая версия, я думаю, даст некоторые странные результаты... - person Clockwork-Muse; 19.11.2011
comment
Вы абсолютно правы насчет того, что моему LEFT JOIN нужно быть на TableB, спасибо. Я не уверен, что вы имеете в виду, говоря о том, что не нужно вкладывать COALESCE(). - person Adam Wenger; 19.11.2011
comment
Первый подход, если вы измените UNION ALL to UNION, должен работать для повторяющихся значений имени. Я не знаю о моем втором подходе, у меня нет рядом sql-сервера для тестирования. Я посмотрю около 5:00 UTC и отвечу тогда. - person Adam Wenger; 19.11.2011
comment
COALESCE() — это функция, которая принимает список аргументов, нет причин разделять его таким образом. Также нет никакой логической разницы в результатах при вложении - a1 всегда будет предпочтительнее, независимо от статуса a2 или a3. Кроме того, TableA as a3 ON b.fkeyA3 = a1.id? Я думаю, что ваше присоединение будет немного запутанным; a2 и a3 будут перекрестно соединяться — вам не нужно присоединяться к TableA еще два раза — подставьте OR условия для оставшихся двух столбцов (и обновите COALESCE()), и все будет хорошо. - person Clockwork-Muse; 19.11.2011
comment
@ X-Zero Я не знал, что Coalesce принимает более двух параметров. Цель исходных объединений заключалась в том, чтобы они были a1, a2 и a3 соответственно, но использование одного с предложением ON было лучше. Спасибо, что научил меня кое-чему сегодня вечером :) - person Adam Wenger; 19.11.2011