2009-06-13 30 views
15

私はすべてのIDをMySQLのツリーだけで子供から取得したいと考えています。子どものすべてのIDを再帰的に見つけるには?

私はこのようなテーブルを持っている:

ID parent_id name 
1 0   cat1 
2 1   subcat1 
3 2   sub-subcat1 
4 2   sub-subcat2 
5 0   cat2 

は、今私は再帰的に(2,3,4)CAT1のためのすべての子IDを取得しようとしています。それを達成する方法はありますか?

答えて

15

これには、隣接リストと入れ子リストの2つの基本的な方法があります。 Managing Hierarchical Data in MySQLをご覧ください。

あなたが持っているものは隣接リストです。いいえ、1つのSQL文ですべての子孫を再帰的に取得する方法はありません。可能であれば、それらをすべて掴んでコードにすべてマッピングしてください。

ネストされたセットは必要なことをすることができますが、レコードを挿入するコストが高く、エラーが発生しやすいので避ける傾向があります。

+0

それをサポートするのRDBMSでプレーンなSQLクエリで再帰を持つ方法があります、 PostgreSQLのように – shesek

+0

このリンクを返すWebページは利用できません – HMagdy

1

これはオプションの場合は、ストアドプロシージャで実行できます。

それ以外の場合は、単一のSQL文では実行できません。

理想的にはあなたの質問は少し不正確思わ

+0

木が大きいとパフォーマンスが低下する可能性があります。 – dm76

-1

あなたのプログラムからツリーを歩いて再帰呼び出しを行う必要があります。なぜあなたはそれらを持っていたいのですか?そして、それらを「木の中に」持つことはどういう意味ですか?

あなたが持っているテーブルは、ツリーを表現するリレーショナルな方法です。

ペア(ID 4、ParentID 0)を保持する行を「テーブル内」にしたい場合は、そのエンジンがサポートするSQLエンジンの再帰SQLバージョンが必要です。

私は特にMySQLについてはわかりませんが、Oracleと同じ構文、つまりCONNECT BYを使用して再帰SQLを実装する予定だったことが私の理解です。

マニュアルの目次で「再帰的クエリ」や「CONNECT BY」などのキーワードを調べると、その答えを見つけることができるはずです。

(もっとすぐに消費答えを提供することができないため申し訳ありません。)

0

答えは基本的にないか、または単一MYSQL文を使用して、少なくとも非常に簡単ではないではないことを見て、私は自分を投稿します階層リストを行うには、PHP/MySQLのコード..

function createCategorySubArray() 
{ 
    $categories = getSQL("SELECT pos_category_id FROM pos_categories"); 
    for($i=0;$i<sizeof($categories);$i++) 
    { 
     //here we need to find all sub categories 
     $pos_category_id = $categories[$i]['pos_category_id']; 
     $cat_list[$pos_category_id] = recursiveCategory($pos_category_id,array()); 

    } 
    return $cat_list; 

} 
function recursiveCategory($pos_category_id, $array) 
{ 
    $return = getSql("SELECT pos_category_id FROM pos_categories WHERE parent = $pos_category_id"); 
    for($i=0;$i<sizeof($return);$i++) 
    { 
     $sub_cat = $return[$i]['pos_category_id']; 
     $array[] = $sub_cat; 
     $array = recursiveCategory($sub_cat, $array); 
    } 
    return $array; 
} 

は、その後、あなたは $ cat_array = createCategorySubArray()でそれを呼び出します。

商品カテゴリに基づくプロモーションがサブカテゴリに適用されているかどうかを確認するには、この情報が必要です。

1

表を作成し、それは

DROP TABLE IF EXISTS `parent_child`; 
CREATE TABLE `parent_child` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `parent_id` int(11) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1; 

insert into `parent_child`(`id`,`name`,`parent_id`) 
values (1,'cat1',0),(2,'subcat1',1), 
(3,'sub-subcat1',2),(4,'sub-subcat2',2), 
(5,'cat2',0); 

の下のように見えるべきである

DELIMITER $$ 

USE `yourdatabase`$$ 

DROP FUNCTION IF EXISTS `GetAllNode1`$$ 

CREATE DEFINER=`root`@`localhost` FUNCTION `GetAllNode1`(GivenID INT) RETURNS TEXT CHARSET latin1 
    DETERMINISTIC 
BEGIN 
    DECLARE rv,q,queue,queue_children TEXT; 
    DECLARE queue_length,front_id,pos INT; 
    SET rv = ''; 
    SET queue = GivenID; 
    SET queue_length = 1; 
    WHILE queue_length > 0 DO 
     SET front_id = queue; 
     IF queue_length = 1 THEN 
      SET queue = ''; 
     ELSE 
      SET pos = LOCATE(',',queue) + 1; 
      SET q = SUBSTR(queue,pos); 
      SET queue = q; 
     END IF; 
     SET queue_length = queue_length - 1; 
     SELECT IFNULL(qc,'') INTO queue_children 
     FROM (SELECT GROUP_CONCAT(id) AS qc 
     FROM `parent_child` WHERE `parent_id` = front_id) A ; 
     IF LENGTH(queue_children) = 0 THEN 
      IF LENGTH(queue) = 0 THEN 
       SET queue_length = 0; 
      END IF; 
     ELSE 
      IF LENGTH(rv) = 0 THEN 
       SET rv = queue_children; 
      ELSE 
       SET rv = CONCAT(rv,',',queue_children); 
      END IF; 
      IF LENGTH(queue) = 0 THEN 
       SET queue = queue_children; 
      ELSE 
       SET queue = CONCAT(queue,',',queue_children); 
      END IF; 
      SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1; 
     END IF; 
    END WHILE; 
    RETURN rv; 
END$$ 

DELIMITER ; 

願望出力

の書き込みクエリを親、子要素を取得するための関数を作成します。ここで
SELECT GetAllNode1(id) FROM parent_child 
or 
SELECT GetAllNode1(id) FROM parent_child where id =1 //for specific parent's child element 
6

単純な単一のクエリのMySQLソリューションです:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

ただ、親要素のID<id>を代用。

ID = <id>の要素のすべての子孫のIDの文字列を,で区切って返します。

ザ・OPはの子供たちのために尋ねたルート/親要素を含む

SELECT * 
FROM `table_name` 
WHERE FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 

:あなたは、むしろ複数の行は、各行に1人の子孫で、返さなければならない場合は、このようなものを使用することができます要素となります。場合によっては、ルート/親要素を結果に含めると便利な場合があります。ここに私の提案の解決策は以下のとおりです。IDの

カンマ区切りの文字列:

SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
    SELECT <id> Level 
    UNION 
    SELECT @Ids := (
     SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
     FROM `table_name` 
     WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
    FROM `table_name` 
    JOIN (SELECT @Ids := <id>) temp1 
) temp2 

複数行:

SELECT * 
FROM `table_name` 
WHERE `ID` = <id> OR FIND_IN_SET(`ID`, (
    SELECT GROUP_CONCAT(Level SEPARATOR ',') FROM (
     SELECT @Ids := (
      SELECT GROUP_CONCAT(`ID` SEPARATOR ',') 
      FROM `table_name` 
      WHERE FIND_IN_SET(`parent_id`, @Ids) 
    ) Level 
     FROM `table_name` 
     JOIN (SELECT @Ids := <id>) temp1 
    ) temp2 
)) 
+0

あなたのソリューションを説明できますか?非常に有望ですが、わかりません。 @Idsは結合のために再帰的に使用されるエイリアス関数/ varであり、どのようなmysqlバージョンがこの種のクエリをサポートしているかどうかに関する情報がありますか? – MonkeyMonkey

+0

@Idsは、[ユーザ定義変数](http://dev.mysql.com/doc/refman/5.7/en/user-variables.html)です。これは、MySQLで長年にわたって使われていました。私は、あなたが使いたいMySQLの任意のバージョンでクエリを実行する必要があると思います。クエリは、最初の行の元のアイテムの子であるすべてのアイテムのIDを連結して動作し、2番目の行の最初の行に含まれるアイテムの子であるすべてのアイテムのIDを連結します。すべての行が1つの行に連結されます。オプションで、各行が独自の行(最後のクエリ)を取得できるように、その行を分割できます。 –

+1

これは素晴らしく素晴らしいですが、私たちはしばらくそれを使っていましたが、今日は最後の入れ子レベルの子が切り捨てられていることに気付きました。 'WHERE'節に' OR FIND_IN_SET(id、@Ids) 'がなければなりません。ありがとう! – Wirone

関連する問題