web-dev-qa-db-ja.com

ストアドプロシージャとCURSORを使用したMySQLの再帰クエリ

ユーザー/グループシステムを拡張して、他のグループメンバーで構成される動的グループを許可しています。 3つのテーブルusersgroups、およびrelationshipsがあります。

簡単にするために、usersは1つのフィールドuser_idのみを含み、groupsにはgroup_idのみが含まれます。 relationshipsは、user_idgroup_idrelated_group_idの3つのフィールドを使用して、両方のユーザーをグループ化しますおよびグループからグループの関係。

動的に入力されるグループ階層は次のようになります。

ユーザーA, B, Cはグループ1に属し、ユーザーD, E, Fはグループ2に属します。これらはすべて関係で表されますテーブルを(user_idgroup_id)として:

A,1
B,1
C,1
D,2
E,2
F,2

動的グループ3には、2つのgroup to group関係レコード(group_idrelated_group_id)があります。

3,1
3,2

私の問題をよりよく説明するために、動的グループ4を追加して、グループ3を含めます。

4,3

MySQL CURSORについて私が学んだことをつなぎ合わせて、fetch_inheritance_groupsと呼ばれる次のストアドプロシージャを作成しました。

DROP PROCEDURE IF EXISTS `fetch_inheritance_groups`;

CREATE PROCEDURE `fetch_inheritance_groups`(IN parent_id INT)
    READS SQL DATA
    BEGIN

    DECLARE inherit_id char(32);
    DECLARE eol BOOLEAN;
    DECLARE inherit_cur CURSOR FOR SELECT r.Related_Group_ID FROM usr_relationships r WHERE r.Group_ID = parent_id AND r.Related_Group_ID IS NOT NULL;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET eol = TRUE;

    OPEN inherit_cur;
    inherited: LOOP
        FETCH inherit_cur INTO inherit_id;

        IF eol 
            THEN
            CLOSE inherit_cur;
            LEAVE inherited;
        ELSE
            CALL fetch_inheritance_groups(inherit_id);
        END IF;

        SELECT inherit_id;

    END LOOP inherited;

END;

...これは...と呼ばれます.

CALL fetch_inheritance_groups(4);

...期待される結果を返します

3
2
1

ここで私の問題が始まります。それらは単一の結果セットの3つの行ではなく、3つの別々の結果セットとして返されます。次に、結果は次のようなクエリ内で使用するためのものです。

SELECT r.uer_id FROM relationships r WHERE r.group_id = 4 OR r.group_id IN (CALL fetch_inheritance_groups(4));

だから私が誰かが答えることを望んでいる3つの質問は:

1。ここでCURSORを使用する必要がありますか、それとも同じ再帰的な結果が得られる代替手段はありますか?

2。副選択と同じように使用できるように、結果を単一の結果セットに戻すにはどうすればよいですか

3。 CALLの結果を使用する適切な方法は何ですか?上記のサンプルSELECTステートメントを機能させるために、少なくとも試してみたのですができないのですか?これは、CALLをインラインで使用できないためだと思いますが、よくわかりません。


UPDATE:ここの関連する質問: 一時的なMEMORYテーブルは、ドロップしない場合、どれくらい持続しますか(MySQL) =上記で使用したスト​​アドプロシージャに一連の更新を投稿しましたが、これはまだ再帰的ですが、IDのリストを変数として提供し、FIND_IN_SET()と互換性のある形式で提供して、必要なものを正確に提供します。

2
oucil

だから私が誰かが答えることを望んでいる3つの質問は:

1.ここでカーソルを使用する必要がありますか、それとも同じ再帰的な結果が得られる代替手段はありますか?

いいえ、できません。 CURSORは何度も開かれ続けています。頭上を考えると私はうんざりします。個人的に、私はカーソルから離れています。

2.副選択と同じ方法で使用できるように、結果を単一の結果セットに戻すにはどうすればよいですか?

3. CALLの結果を使用する適切な方法は何ですか?上記のサンプルSELECTステートメントを機能させるために、少なくとも試してみたのですが、できません。これは、CALLをインラインで使用できないためだと思いますが、よくわかりません。

私は、大学時代にデータ構造について学んだときに私が学んだことを提案することで、あなたを救済したいと思います。キューを使用してツリートラバーサルを実行する必要があります。これにより、ルートから開始して、ツリーのすべての子孫を表すことができます。

アルゴリズムはこのようになります

  • ステップ01:空のキューから開始
  • ステップ02:デキューNodeキューの前から
  • ステップ03:最新のノードのすべての子をエンキューする
  • ステップ04:最新のノードからの情報を処理する
  • ステップ05:キューが空でない場合は、ステップ02に戻ります。
  • ステップ06:すべて完了

これにより、プログラムによる再帰を使用せずに再帰構造をトラバースできます。この時点で、あなたはおそらく尋ねています:再帰せずにツリー構造をトラバースするにはどうすればよいですか?

ノードのあるテーブルとテーブル内のその親をトラバースする単一のCALLでループを使用できる3つのストアドプロシージャをスクリプト化する方法について投稿しました。

  • GetParentIDByID
  • GetAncestry
  • GetFamilyTree

投稿は 階層フィールドの最高レベルを検索:CTEありvsなし (2011年10月24日)。これには、次のテーブル構造をたどる、すでに記述されているストアドプロシージャが含まれています。

+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment | 
| parent_id  | int(11)      | YES  |     | NULL    |                | 
| name       | varchar(255) | YES  |     | NULL    |                | 
| notes      | text         | YES  |     | NULL    |                | 
+------------+--------------+------+-----+---------+----------------+

コードを注意深く読み、原則を適用してください。

試してみる !!!

3
RolandoMySQLDBA