2009-06-03 24 views
0

私はmySQLを使っていますが、他のテーブルのparent - > childデータ階層の任意のレベルのIDと一致する1つのテーブルからデータを選択する必要があります。MySQL parent - > child query

さらに、私はこの機能がかなり使用されるので、PHPコードの再帰関数ではなく、よく書かれたSQLクエリでこれを解決したいと思います。

私は検索を試みましたが、多くの類似した問題(ほとんど解決されました)に遭遇しましたが、どれも私を助けませんでした。状況を説明するのを助けるために

は、ここに私の現在の設定

テーブル "記事" です:

  • のarticle_id
  • CATEGORY_ID
  • ...

テーブルカテゴリ

  • は、私は、「articles.category_id」は「記事」からのすべての記事を選択するのは、言わせて、10しかし、また、すべての記事を受信する必要が... PARENT_ID

  • をCATEGORY_ID "categories.category_id" 10が属するツリーのすべてのカテゴリから選択します。

    意味(ここで、「10」は親であり、すべてが子であり、上は10が子であり、そのすべてが親である)。

    再帰的なPHP関数なしで可能ですか?

    ありがとうございます。

  • 答えて

    2

    これはMySQLに行うことが可能ですが、それは少し手間がかかり。

    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 categories 
           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 categories 
           WHERE id = _parent; 
         END LOOP; 
    END 
    

    をし、クエリでそれを使用する:あなたはこのように関数を記述する必要があります

    SELECT id, 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, categories 
         WHERE @id IS NOT NULL 
         ) ho 
    JOIN categories hi 
    ON  hi.id = ho.id 
    

    は詳細のための私のブログにこのエントリを参照してください:

    +0

    これは非常に興味深いようです、努力をいただきありがとうございます! 私はインバウンドと同様の状況の別のプロジェクトを持っており、私はこれを試してみてください。 –

    +0

    私の問題に対する完璧な答えのように見えます。しかし、DECLARE文にエラーが発生しています。DECLARE id INT \tエラーコード:1064. SQL構文にエラーがあります。使用する正しい構文についてはMySQLサーバのバージョンに対応するマニュアルを確認してください –

    +0

    自分のコメントに答える:この機能を実行する前にDELIMITERを変更する必要があるようです(DELIMITER //) –

    3

    MySQLを使用しているため、使用している隣接関係リストデザインを使用して1つのクエリでツリー全体をフェッチすることはできません。

    データベースのいくつかの他のブランドは、この種の設計を処理するSQL拡張をサポートしています。 Oracle、Microsoft SQL Server、IBM DB2、およびPostgreSQL 8.4(現在ベータ版)はSQL拡張をサポートしています。

    ツリーをより効率的にクエリできる他のデータベース設計が存在します。この質問は、StackOverflow、ブログ、および記事で何度も述べられています。

    Joe Celkoの "Trees and Hierarchies in SQL for Smarties"も読んでいただけます.Joe Celkoはこのようなデザインをいくつか取り上げています。

    +0

    私が私の答えで話していた機能の流れがあれば、私は興味があります。私はあなたがデータベースよりもはるかに多くの作業経験があることを知っています。 –

    +0

    @Ionut:さて、あなたの答えに私のコメントを見てください。 –

    +0

    ビル、それを見て時間を割いてくれてありがとう。ほんとうにありがとう。 –

    0

    これはどれくらい助けてくれるのか分かりませんが、私は単一のMySQLクエリを使って階層ツリーを生成する小さな関数を書いています。基本的に、すべての重要なロジックはPHPに移行されます。私のソリューションは隣接リストモデルを使用して、フラットなものを使ってツリーデータ構造を構築するためにPHP参照を利用しています。下の要点を見て、インスピレーションを得るかどうかを見てください。私はあなたにもっとお手伝いしたいと思いますが、私が仕事で取り組まなければならない問題がいくつかあります。

    http://gist.github.com/104357

    +0

    さて、あなたのコードを読んだ。目的の階層のノードだけを含むクエリをどのように記述しますか?たとえば、Slashdotを実行しているとします。何千もの別々の階層に配置された数百万のコメントが、単一のデータベーステーブルに格納されています。ユーザーが現在表示しているスレッドに属するコメントのみをどのように照会しますか? –

    +0

    これは実装の弱点です。大きなデータセットの場合、メモリ内のテーブルデータ全体をフェッチするのはあまり意味がありません。私の解決策はそれを仮定している。私が書いた時には、小さなデータ量のテーブル(小さなものが実際に何を意味するのかはわかりません)が考えられました。複数のクエリの中で、すべてのクエリを1つのクエリで取得するほうが良いと思います。ある特定の「スレッド」のための到達は、PHPコードで行われます。 –

    +0

    一度にすべてのデータを一度に取得する方が良いかどうかは、データがどれだけ多いかによって決まります。 :-)これを緩和するためにできることの1つは、 "parent_id"に加えて各ツリーノードに "root_id"を保存することです。そのようにして、同じ "root_id"を持つすべての行を照会し、結果に必要なツリーのみが含まれていることを確認することができます。 PHPのメモリを超える巨大なツリーが1つでも得られるかもしれませんが、少なくとも関連のないツリーのデータも取得しません。 –