MySQL INSERT ... SELECT из 1 таблицы в 2 таблицы
Я вижу довольно много вопросов, касающихся вставки в одну таблицу из нескольких таблиц. Я пытаюсь сделать наоборот, и использую только MySQL.
У меня есть временная таблица, которая содержит денормализованные данные. Мне необходимо
- перебирать каждую строку временной таблицы
- создать новую строку в первичной таблице с некоторыми полями во временной таблице
- используйте идентификатор автоинкремента только что созданной строки первичной таблицы, чтобы создать новую строку во вторичной таблице, которая помещает
LAST_INSERT_ID()
в поле primary_id.
Я понимаю весь LAST_INSERT_ID()
и я счастлив запустить транзакцию: я просто не знаю, как создать "внешний цикл выбора", который циклически перебирает временную таблицу и затем выполняет 2 последующие вставки.
2 ответа
Это может работать, если значения во временной таблице уникальны. Идея состоит в том, чтобы разделить вставки на два шага, один в основной таблице, а остальные в дополнительную таблицу:
insert into primary( . . .)
select . . .
from temp;
insert into secondary(primaryid, . . . )
select p.PrimaryId, t.col . . .
from temp t join
primary p
on t.col1 = p.col1 and . . .;
Есть несколько предостережений. Например, вам понадобится более сложная логика для обработки соединений. И это предполагает, что каждый набор первичных столбцов во временной таблице является уникальным.
Наиболее распространенным подходом является цикл в хранимой процедуре или код приложения, который использует LAST_INSERT_ID
,
Как сказал @GordonLinoff, можно зацикливаться LAST_INSERT_ID()
, Если каждый набор первичных столбцов во временной таблице может быть не уникальным, по сути, есть два способа сделать это:
Положитесь на ограничение уникальности для соответствующих столбцов в первичной таблице вместе с MySQL
ON DUPLICATE KEY UPDATE
расширение доINSERT
:CREATE PROCEDURE foo() BEGIN DECLARE done BOOLEAN DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT . . . FROM temp; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE; PREPARE ins1 FROM ' INSERT INTO primary (. . .) VALUES (?, . . .) ON DUPLICATE KEY UPDATE PrimaryId = LAST_INSERT_ID(PrimaryId) '; PREPARE ins2 FROM ' INSERT INTO secondary (primaryid, . . .) VALUES (LAST_INSERT_ID(), ?, . . .) '; OPEN cur; read_loop: LOOP FETCH cur INTO @a, @b, . . .; IF done THEN LEAVE read_loop; END IF; EXECUTE ins1 USING @a, . . .; EXECUTE ins2 USING @b, . . .; END LOOP; CLOSE cur; DROP PREPARE ins1; DROP PREPARE ins2; END
Сортируйте по временной таблице (неуникальные) первичные столбцы, затем отслеживайте последние просмотренные значения и вставляйте в первичную таблицу только при обнаружении новой записи:
CREATE PROCEDURE foo() BEGIN DECLARE done BOOLEAN DEFAULT FALSE; DECLARE cur CURSOR FOR SELECT . . . FROM temp ORDER BY . . .; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done := TRUE; PREPARE ins1 FROM ' INSERT INTO primary (. . .) VALUES (. . .) -- use "?" placeholders '; PREPARE ins2 FROM ' INSERT INTO secondary (primaryid, . . .) VALUES (. . .) -- use "?" placeholders '; OPEN cur; FETCH cur INTO @a, @b, . . .; WHILE NOT done DO SET @current_a := @a, . . .; -- (non-unique) primary cols EXECUTE ins1 USING @a, . . .; SET @primaryid := LAST_INSERT_ID(); REPEAT EXECUTE ins2 USING @primaryid, @b, . . .; FETCH cur INTO @a, @b, . . .; UNTIL done OR @a <> @current_a OR . . . END REPEAT; END WHILE; CLOSE cur; DROP PREPARE ins1; DROP PREPARE ins2; END