Переместить узел во вложенном множестве - PullRequest
23 голосов
/ 20 мая 2009

Мне нужен запрос MySQL, который перемещает узел и все его дочерние элементы во вложенном наборе. Я нашел этот сайт, но эта функция кажется просто нелогичной - в модели с вложенным множеством нет universeid или treeid, а сам код просто длиннее, чем требуется. Единственный дополнительный столбец, который у меня есть в таблице, это parent.

Я не мог просто удалить и снова добавить узел, так как он потеряет свой идентификатор.

Ответы [ 13 ]

0 голосов
/ 04 октября 2009

Я полагаю, что с двумя дополнительными столбцами для хранения исходного значения правого и левого узлов (и всех последующих подузлов) алгоритм можно упростить. Я работал с примерами карандашом и бумагой, поэтому, если вы обнаружите какие-либо дыры в алгоритме, пожалуйста, дайте мне знать.

Целевым узлом (новым родительским узлом перемещаемого вами узла) является tNode. Левое значение целевого узла - tNode.L, а правое - tNode.R. Точно так же узел, который вы перемещаете, это mNode, а значения mNode влево и вправо - mNode.L и mNode.R. Два дополнительных столбца - это mNode.SL и mNode.SR

Итак, всего у нас есть 4 столбца для манипуляций R, L, SL и SR


Step1

Рассчитать

delta1 = (mNode.R - mNode.L) + 1 

Step2

Сохранение оригинальных m и узлов L и R в столбцы SL и SR

- For All L between mNode.L and mNode.R 
   mNode.SL = mNode.L ; mNode.L = 0 ;
 - For All R between mNode.L and mNode.R 
   mNode.SR = mNode.R ; mNode.R = 0 ;

Step3

Do For all Nodes
IF L > mNode.SR 
   L = L + delta1
IF R > mNode.SR
   R = R + delta1

Теперь mNode отсоединен от Tree, и дерево настраивается без mNode.

Step4

Рассчитать

delta2 = (tNode.R - mNode.SL)

Step5

Do for all Nodes
  IF L >= tNode.R
    L = L + delta1
  IF R >= tNode.R
    R = R + delta1

Теперь мы настроили дерево (и целевой узел), чтобы принимать количество удаленных узлов.

Step6

Присоединить mNode к tNode и сбросить значения столбцов SL / SR

Do for all Nodes
 IF SL between mNode.SL and mNode.SR
    L = mNode.SL + delta2 ; mNode.SL = 0  ;
 IF SR between mNode.SL and mNode.SR
    R = mNode.SR + delta2 ; mNode.SR = 0 ;

После всех этих шагов мы должны были переместить mNode под tNode.

0 голосов
/ 23 мая 2009

У меня есть хранимая процедура, которая перемещает узел из вложенного набора в новый родительский узел. Я использую таблицу с именем «category» в базе данных MySQL / InnoDB под названием «somedb». Конечно, если пункт назначения является подкатегорией категории, которую вы хотите переместить, эта процедура испортит ситуацию, поэтому убедитесь, что вы не пытаетесь встроить узел внутрь себя. Я оставлю это в качестве упражнения для читателя, чтобы сделать эту процедуру безопасной для этого случая.

CREATE PROCEDURE `somedb`.`moveCatParent` (IN cat_a VARCHAR(45), IN cat_b VARCHAR(45))
BEGIN
    START TRANSACTION;

    /* cat_b.lft + 1 is the destination. */
    SELECT @destination := (lft + 1)
    FROM category
    WHERE name = cat_b;

    SELECT @cat_a_width := ((rgt - lft) + 1)
    FROM category
    WHERE name = cat_a;

    /* Rip this table a new cat_a sized hole inside cat_b. */  
    UPDATE category SET rgt = rgt + @cat_a_width WHERE rgt >= @destination;
    UPDATE category SET lft = lft + @cat_a_width WHERE lft >= @destination;

    SELECT @cat_a_lft := lft, @cat_a_rgt := rgt
    FROM category
    WHERE name = cat_a;

    SELECT @diff := @destination - @cat_a_lft;

    /* Move cat_a and all inhabitants to new hole */  
    UPDATE category SET rgt = rgt + @diff WHERE rgt BETWEEN @cat_a_lft AND @cat_a_rgt;
    UPDATE category SET lft = lft + @diff WHERE lft BETWEEN @cat_a_lft AND @cat_a_rgt;

    /* Close the gap created when we moved cat_a. */
    UPDATE category SET rgt = rgt - @cat_a_width WHERE rgt >= @cat_a_lft;
    UPDATE category SET lft = lft - @cat_a_width WHERE lft >= @cat_a_lft;

    COMMIT;
END
0 голосов
/ 20 мая 2009

См. Статью в моем блоге для хранения и использования иерархических данных в MySQL:

Чтобы переместить целую ветвь в такой таблице, вам просто нужно обновить корень parent (одна строка)

Вам нужно будет создать функцию:

CREATE FUNCTION hierarchy_connect_by_parent_eq_prior_id(value INT) RETURNS INT
NOT DETERMINISTIC
READS SQL DATA
BEGIN
        DECLARE _id INT;
        DECLARE _parent INT;
        DECLARE _next INT;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET @id = NULL;

        SET _parent = @id;
        SET _id = -1;

        IF @id IS NULL THEN
                RETURN NULL;
        END IF;

        LOOP
                SELECT  MIN(id)
                INTO    @id
                FROM    t_hierarchy
                WHERE   parent = _parent
                        AND id > _id;
                IF @id IS NOT NULL OR _parent = @start_with THEN
                        SET @level = @level + 1;
                        RETURN @id;
                END IF;
                SET @level := @level - 1;
                SELECT  id, parent
                INTO    _id, _parent
                FROM    t_hierarchy
                WHERE   id = _parent;
        END LOOP;
END

и использовать его в запросе:

SELECT  CONCAT(REPEAT('    ', level - 1), CAST(hi.id AS CHAR)) AS treeitem, parent, level
FROM    (
        SELECT  hierarchy_connect_by_parent_eq_prior_id(id) AS id, @level AS level
        FROM    (
                SELECT  @start_with := 0,
                        @id := @start_with,
                        @level := 0
                ) vars, t_hierarchy
        WHERE   @id IS NOT NULL
        ) ho
JOIN    t_hierarchy hi
ON      hi.id = ho.id
...