Хранимая процедура Mysql для копирования записей в иерархии Parent / Child / Grandchild - PullRequest
5 голосов
/ 18 апреля 2011

У меня есть 3 таблицы родительский, дочерний и внучатый в форме:

+----------------+   +----------------+   +---------------------+
| 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, если это важно.

Ответы [ 3 ]

4 голосов
/ 18 апреля 2011

Попробуйте этот запрос 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 голосов
/ 25 апреля 2011

С тех пор я узнал немного больше, и я считаю, что вклад @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;
0 голосов
/ 19 апреля 2011

РЕДАКТИРОВАТЬ : С тех пор я создал еще один ответ, который, я считаю, проще и лучше, чем этот.

Завершенная хранимая процедура с использованием ответа @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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...