1

MySQL で、ネストされたセット モデルの問題に直面しています。サブツリーを別の親に挿入、削除、移動できます。すべて正常に動作します。

しかし、兄弟を注文する方法がわかりません。たとえば、私はこれらの兄弟を持っています:

A、B、C、D、E

そして、私はこれを取得して、Dの後にBを移動したい:

A、C、D、B、E

挿入、削除などのための大量のストアド プロシージャを見つけましたが、兄弟を注文するストアド プロシージャは 1 つもありませんでした。私が見つけた唯一のものは兄弟を交換するための手順ですが、それは私が達成したいことではありません.

独自のものを作成しようとしましたが、複雑に見え、すべての場合に機能するとは限りません。

彼の兄弟の 1 人の前または後にノードを移動する方法を知っていれば、これは大歓迎です。

4

2 に答える 2

5

だから...私はすべてを書き直しました。これは、彼の兄弟の1つのノードの後に​​1つのノードを移動するのにうまく機能するストアドプロシージャです。ノードを最初の位置に移動したい場合は、兄弟 ID の代わりに親 ID を渡すだけです。

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 ;

おそらく、これはこれまでに見た中で最も美しいコードではありませんが、問題なく動作し、たとえば、どの種類の並べ替えアルゴリズムよりもはるかに効率的です。

于 2011-05-14T10:29:23.680 に答える
0

トリガーを使用してネストされたセットを処理する場合は、次のことをお勧めします。

  1. 並行性の問題を回避するために、必要に応じて before トリガーを使用して行をロックします。
  2. after トリガーを使用してインデックスを再作成し、一括挿入/一括更新関連の問題を回避します。
  3. インデックスを再作成する前に、最後の after トリガーが起動するのを待ちます。
  4. その時点で、ステートメントによって複数のノードが挿入/移動されたことを検出した場合は、該当する範囲全体のインデックスを再作成します。これは、ツリーのチャンク全体を複数回再インデックスするよりもはるかに高速です。

特にあなたの質問については、私がうまくいけば、ストアドプロシージャを使用してノードを1つずつ移動しています。ノードの親を変更するのと大差ありません。新しい lft/rgt インデックスを見つけて、それに応じて (およびその子ノードを) 左または右にシフトします。右にシフトしている場合は、lft/rgt 値をオフセットすることを忘れないでください。

余談ですが、解決する必要がある潜在的な問題を特定し始めたばかりだと思います。単一の更新を使用したノードの順列は、私の経験では最も扱いにくいものです。たとえば、次のようになります。

A - B - C

D - E - F

に:

B - E - C

A - D - F
于 2011-05-14T09:47:46.610 に答える