Заказ братьев и сестер в модели вложенного набора - PullRequest
1 голос
/ 14 мая 2011

У меня проблема с моделью вложенного множества, с MySQL.Я могу вставить, удалить, переместить поддерево другому родителю, все работает нормально.

Но я не могу понять, как заказать братьев и сестер.Например, у меня есть эти братья и сестры:

A, B, C, D, E

И я хочу переместить B после D, получив это:

A, C, D, B, E

Я нашел тонны хранимых процедур для вставки, удаления и т. Д., Но ни одной из них не было, чтобы упорядочить братьев и сестер.Единственный, который я нашел, - это процедура обмена братьями и сестрами, но это не то, чего я хочу достичь.

Я пытался написать свою собственную, но она кажется сложной и работает не во всех случаях.

Если вы знаете, как перемещать узел до или после одного из его братьев и сестер, это будет очень ценно.

Ответы [ 2 ]

5 голосов
/ 14 мая 2011

Итак ... Я переписал все, и вот хранимая процедура, которая хорошо работает для перемещения одного узла после одного из его братьев и сестер.Если мы хотим переместить узел на первую позицию, просто передайте родительский идентификатор вместо идентификатора родного брата.

DELIMITER |
-- sibling parameter is either :
-- - the sibling id after which we want to put the page
-- - the parent id if we want to put the page on the first position
CREATE PROCEDURE move_after_sibling (IN to_move_id INT(10), IN parent_id INT(10), IN sibling_id INT(10))
LANGUAGE SQL
DETERMINISTIC
BEGIN
    DECLARE to_move_lft INT(10);
    DECLARE to_move_rgt INT(10);
    DECLARE parent_lft INT(10);
    DECLARE parent_rgt INT(10);
    DECLARE sibling_lft INT(10);
    DECLARE sibling_rgt INT(10);

    SET to_move_lft = (SELECT lft FROM pages WHERE id = to_move_id);
    SET to_move_rgt = (SELECT rgt FROM pages WHERE id = to_move_id);
    SET parent_lft = (SELECT lft FROM pages WHERE id = parent_id);
    SET parent_rgt = (SELECT rgt FROM pages WHERE id = parent_id);
    SET sibling_lft = (SELECT lft FROM pages WHERE id = sibling_id);
    SET sibling_rgt = (SELECT rgt FROM pages WHERE id = sibling_id);

    UPDATE pages 
        SET
            lft = 
                CASE 
                    WHEN sibling_id = parent_id THEN
                        CASE
                            WHEN lft BETWEEN parent_lft+1 AND to_move_lft-1 THEN
                                lft + (to_move_rgt - to_move_lft) + 1
                            WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN 
                                lft - (to_move_lft - (parent_lft + 1))
                            ELSE
                                lft
                        END
                    ELSE
                        CASE 
                            WHEN to_move_lft > sibling_lft THEN
                                CASE
                                    WHEN lft BETWEEN sibling_rgt AND to_move_lft-1 THEN
                                        lft + (to_move_rgt - to_move_lft) + 1
                                    WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN 
                                        lft - (to_move_lft - (sibling_rgt + 1))
                                    ELSE
                                        lft
                                END
                            ELSE
                                CASE
                                    WHEN lft BETWEEN to_move_rgt+1 AND sibling_rgt THEN
                                        lft - ((to_move_rgt - to_move_lft) + 1)
                                    WHEN lft BETWEEN to_move_lft AND to_move_rgt THEN 
                                        lft + (sibling_rgt - to_move_rgt)
                                    ELSE
                                        lft
                                END
                        END
                END,
            rgt = 
                CASE 
                    WHEN sibling_id = parent_id THEN
                        CASE
                            WHEN rgt BETWEEN parent_lft+1 AND to_move_lft-1 THEN
                                rgt + (to_move_rgt - to_move_lft) + 1
                            WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN 
                                rgt - (to_move_lft - (parent_lft + 1))
                            ELSE
                                rgt
                        END
                    ELSE
                        CASE 
                            WHEN to_move_rgt > sibling_lft THEN
                                CASE
                                    WHEN rgt BETWEEN sibling_rgt+1 AND to_move_lft-1 THEN
                                        rgt + (to_move_rgt - to_move_lft) + 1
                                    WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN 
                                        rgt - (to_move_lft - (sibling_rgt + 1))
                                    ELSE
                                        rgt
                                END
                            ELSE
                                CASE
                                    WHEN rgt BETWEEN to_move_rgt+1 AND sibling_rgt+1 THEN
                                        rgt - ((to_move_rgt - to_move_lft) + 1)
                                    WHEN rgt BETWEEN to_move_lft AND to_move_rgt THEN 
                                        rgt + (sibling_rgt - to_move_rgt)
                                    ELSE
                                        rgt
                                END
                        END
                END
        WHERE lft BETWEEN parent_lft+1 AND parent_rgt;
END
|
DELIMITER ;

Возможно, это не самый красивый фрагмент кода, который мы когда-либо видели, но этоработает нормально и, вероятно, намного эффективнее, чем любой алгоритм сортировки, например.

0 голосов
/ 14 мая 2011

Я обнаружил, что при работе с вложенными множествами с использованием триггеров лучше:

  1. Используйте триггеры перед, чтобы блокировать строки по мере необходимости, чтобы избежать проблем параллелизма.
  2. Используйте после триггеров для переиндексации, чтобы обойти проблемы, связанные с массовой вставкой / массовым обновлением.
  3. Дождитесь срабатывания самого последнего после триггера, прежде чем что-либо переиндексировать.
  4. Если в этот момент вы обнаружите, что с помощью оператора было вставлено / перемещено более одного узла, то можно повторно проиндексировать применимый диапазон в целом - это происходит намного быстрее, чем многократное повторное индексирование целых блоков дерева. 1010 *

В частности, для вашего вопроса вы перемещаете узлы один за другим, используя хранимую процедуру, если я правильно понял. Это не очень отличается от изменения родительского узла: найдите его новый индекс lft / rgt и сместите его (и его дочерние узлы) соответственно влево или вправо. Не забудьте сместить значения lft / rgt, если они сдвинуты вправо.

Кроме того, я подозреваю, что вы только начинаете определять потенциальные проблемы, которые вам нужно решить. По моему опыту, с перестановками узлов, использующими одно обновление, сложнее всего справиться, например: * 10101

A - B - C

D - E - F

Кому:

B - E - C

A - D - F
...