2011-04-18 101 views
5

複製記錄我有3個表的父,子&孫子的形式爲:MySQL存儲過程中的父/子/孫層次

+----------------+ +----------------+ +---------------------+ 
| 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。

回答

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聲明,或嘗試自己做; )

+0

完美地工作,儘管它需要我花一段時間才能完全理解它!我現在使用完整的存儲過程,使用你的代碼,我將在下面添加另一個答案。 – David 2011-04-18 23:10:26

+0

問題是 - 如何綁定兩個子表以獲取新的child_id。我試圖通過他們的posituin號碼(排名字段)來綁定行。 – Devart 2011-04-19 07:23:10

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 
+0

@Devart你認爲這將可能擴大到包括一個偉大的孩子表? – David 2011-04-18 23:31:56

+0

我認爲它可以以某種方式完成。但現在 - 這是一個問題;) – Devart 2011-04-19 13:31:26

0

我自從學會了一點,我相信從@Devart的貢獻可以簡化一點。我現在使用的解決方案如下所示。

注意:如果使用的表是InnoDB而不是MyISAM,此解決方案才能正常工作。我認爲這與MyISAM的默認排序行爲的工作方式有關。 (InnoDB使用主鍵,而MyISAM使用插入順序。)特別是,使用MyISAM表時,'副本孫'(s)'部分生成的兩組等級不相互'同步'。在相關部分中添加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:[email protected]+1 as rank FROM child WHERE parent_id=p_parent_id) c1 
     INNER JOIN 
     (SELECT child_id, @rank2:[email protected]+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:[email protected]+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:[email protected]+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;