我看到很多关于从多个表中插入单个表的问题。我试图做相反的事情,并且只使用 MySQL。
我有一个包含非规范化数据的临时表。我需要
- 遍历临时表的每一行
- 使用临时表中的一些字段在主表上创建一个新行
- 使用刚刚创建的主表行的自动增量 ID 在辅助表上创建一个新行,将其
LAST_INSERT_ID()
放入 primary_id 字段。
我了解整体LAST_INSERT_ID()
,我很高兴运行一个事务:我只是不知道如何创建循环通过临时表然后运行 2 个后续插入的“外部选择循环”。
我看到很多关于从多个表中插入单个表的问题。我试图做相反的事情,并且只使用 MySQL。
我有一个包含非规范化数据的临时表。我需要
LAST_INSERT_ID()
放入 primary_id 字段。我了解整体LAST_INSERT_ID()
,我很高兴运行一个事务:我只是不知道如何创建循环通过临时表然后运行 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