我有三张桌子父母,儿童和儿童形式的孙子:
+----------------+ +----------------+ +---------------------+
| parent | | child | | grandchild |
+----------------+ +----------------+ +---------------------+
| parent_id (PK) | | child_id (PK) | | grandchild_id (PK) |
| parent_data | | child_data | | grandchild_data |
+----------------+ | parent_id (FK) | | child_id (FK) |
+----------------+ +---------------------+
PK =自动递增主键。
FK =外键。
我想要一个存储过程,它可以复制父表中的记录以及子表和孙表中的任何相关记录。我可以将父数据和子数据复制好,这是我正在努力的孙子表。就我而言:
CREATE FUNCTION sf_copy_parent(p_parent_id INT) RETURNS INT
BEGIN
DECLARE new_parent_id INT;
-- create new parent record
INSERT INTO parent(parent_data)
SELECT parent_data FROM parent
WHERE parent_id=p_parent_id;
SET new_parent_id=LAST_INSERT_ID();
-- copy child records
INSERT INTO child(child_data,parent_id)
SELECT child_data,new_parent_id FROM child
WHERE parent_id=p_parent_id;
-- copy grandchild records ???
-- return
RETURN new_parent_id;
END
如果这很重要,我正在使用Mysql5.5。
答案 0 :(得分:4)
尝试此SELECT查询(它使用'p_parent_id'和'new_parent_id'变量) -
SET @r1 = 1;
SET @child_id = NULL;
SET @r2 = 0;
SELECT c1.grandchild_data, c2.child_id FROM (
SELECT @r1 := if(c.child_id IS NULL OR c.child_id <> @child_id, @r1 + 1, @r1) rank, @child_id := c.child_id, c.child_id, g.grandchild_data FROM child c
JOIN grandchild g
ON c.child_id = g.child_id
WHERE
c.parent_id = p_parent_id
ORDER BY
c.child_id, g.grandchild_id
) c1
JOIN (SELECT @r2 := @r2 + 1 rank, child_id FROM child WHERE parent_id = new_parent_id ORDER BY child_id) c2
ON c1.rank = c2.rank;
如果有效,我们会将其重写为INSERT..SELECT语句,或者尝试自己动手;)
答案 1 :(得分:0)
编辑:我已经创建了另一个我认为比这个更简单,更好的答案。
完成后的存储过程然后使用来自@Devart的答案:
CREATE FUNCTION `sp_copy`(p_parent_id INT) RETURNS int(11)
BEGIN
DECLARE new_parent_id INT;
-- create new parent record
INSERT INTO parent(parent_data)
SELECT parent_data FROM parent
WHERE parent_id=p_parent_id;
SET new_parent_id=LAST_INSERT_ID();
-- copy child records
INSERT INTO child(child_data,parent_id)
SELECT child_data,new_parent_id FROM child
WHERE parent_id=p_parent_id;
-- copy grandchild records
SET @r1 = 1;
SET @child_id = NULL;
SET @r2 = 0;
INSERT INTO grandchild(grandchild_data,child_id) SELECT c1.grandchild_data, c2.child_id FROM (
SELECT @r1 := if(c.child_id IS NULL OR c.child_id <> @child_id, @r1 + 1, @r1) rank, @child_id := c.child_id, c.child_id, g.grandchild_data FROM child c
JOIN grandchild g
ON c.child_id = g.child_id
WHERE
c.parent_id = p_parent_id
ORDER BY
c.child_id, g.grandchild_id
) c1
JOIN (SELECT @r2 := @r2 + 1 rank, child_id FROM child WHERE parent_id = new_parent_id ORDER BY child_id) c2
ON c1.rank = c2.rank;
-- return new parent id
RETURN new_parent_id;
END
答案 2 :(得分:0)
我已经学到了更多,我相信@Devart的贡献可以简化一点。我现在使用的解决方案如下所示。
注意:如果使用的表是InnoDB而不是MyISAM,则此解决方案只能正常工作。我认为这与MyISAM的默认排序行为如何工作有关。 (InnoDB使用主键,而MyISAM使用插入顺序。)特别是,对于MyISAM表,“复制孙子”部分中生成的两组排名彼此不“同步”。在相关部分中添加SORT BY子句似乎也没有什么区别。
CREATE PROCEDURE sp_copy(p_parent_id INT)
BEGIN
DECLARE new_parent_id INT;
-- copy parent
INSERT INTO parent(parent_data) SELECT parent_data FROM parent WHERE parent_id=p_parent_id;
SET new_parent_id:=LAST_INSERT_ID();
-- copy child(s)
INSERT INTO child(child_data, parent_id)
SELECT child_data, new_parent_id FROM child WHERE parent_id=p_parent_id;
-- copy grandchild(s)
SET @rank1:=0;
SET @rank2:=0;
INSERT INTO grandchild(grandchild_data, child_id) SELECT gc.grandchild_data, c2.child_id FROM
(SELECT child_id, @rank1:=@rank1+1 as rank FROM child WHERE parent_id=p_parent_id) c1
INNER JOIN
(SELECT child_id, @rank2:=@rank2+1 as rank FROM child WHERE parent_id=new_parent_id) c2 ON c1.rank=c2.rank
INNER JOIN grandchild gc ON c1.child_id=gc.child_id;
END
此外,为了处理一个伟大的子表,可以使用复制孙子记录的相同原则。唯一额外的复杂性是在两个子查询中的每一个中添加连接。这是必需的,因为child.parent_id字段需要在WHERE子句中引用:
-- copy greatgrandchild(s)
SET @rank1:=0;
SET @rank2:=0;
INSERT INTO greatgrandchild(greatgrandchild_data, grandchild_id) SELECT ggc.greatgrandchild_data, gc2.grandchild_id FROM
(SELECT grandchild_id, @rank1:=@rank1+1 as rank FROM grandchild INNER JOIN child ON child.child_id=grandchild.child_id WHERE parent_id=p_parent_id) gc1
INNER JOIN
(SELECT grandchild_id, @rank2:=@rank2+1 as rank FROM grandchild INNER JOIN child ON child.child_id=grandchild.child_id WHERE parent_id=new_parent_id) gc2 ON gc1.rank=gc2.rank
INNER JOIN greatgrandchild ggc ON gc1.grandchild_id=ggc.grandchild_id;