web-dev-qa-db-ja.com

単一のクエリで、MySQLのツリー構造テーブルを任意の深さまでクエリできますか?

私は答えはノーだと思っていますが、誰もがSQL(MySQL)の任意の深さまでツリー構造をクロールする方法についての洞察を持っていたが、単一のクエリでそれが大好きです

より具体的には、ツリー構造のテーブル(id、data、data、parent_id)、およびテーブル内の1つの行を指定すると、all子孫(child/grandchild/etc)、またはそのために取得できますすべての祖先(親/祖父母/など)は、単一のクエリを使用して、それがどれだけ上下するかを知らずに重要ですか?

または、新しい結果がなくなるまでより深くクエリを続ける、何らかの再帰要求を使用していますか?

具体的には、RubyとRailsを使用していますが、それはあまり意味がないと思います。

58
Cameron Booth

はい、これは可能です。これは、ここで最もよく説明されているように、変更された事前順序ツリートラバーサルと呼ばれます。

Joe CelkoのSQL for Smartiesのツリーと階層

実用的な例(PHP)がここに提供されています

http://www.sitepoint.com/article/hierarchical-data-database/2/

37
Dave Cheney

以下にいくつかのリソースを示します。

基本的に、ストアドプロシージャまたはクエリで何らかのカーソルを実行するか、隣接テーブルを作成する必要があります。 dbの外での再帰は避けたいと思います。ツリーの深さによっては、本当に遅く/スケッチする可能性があります。

23
Aaron Jensen

ダニエル・ビアズリーの答えは、あなたが尋ねている主な質問が「私の子供たちは何ですか」と「私の両親は何ですか」である場合、まったく悪い解決策ではありません。

Alex Weinsteinに対応して、この方法は実際には、Celko手法よりも親の動きのノードの更新が少なくなります。 Celkoの手法では、左端のレベル2ノードが右端のレベル1ノードの下に移動した場合、ノードの子だけでなく、ツリー内のほとんどすべてのノードを更新する必要があります。

ただし、ダニエルはルートへのパスを間違った方法で保存している可能性があります。

クエリが次のようになるように保存します

SELECT FROM table WHERE ancestors LIKE "1,2,6%"

これは、mysqlが「先祖」列のインデックスを使用できることを意味します。これは、先頭の%では実行できません。

3
Kray

前にこの問題に出くわし、1つの奇抜なアイデアがありました。 直接の祖先のIDを連結した文字列である各レコードのフィールドをルートに戻すことができます。

このようなレコードがあると想像してください(インデントは階層を意味し、数字はid、祖先です)。

  • 1、「1」
    • 2、「2,1」
      • 5、「5、2、1」
      • 6、「6,2,1」
        • 7、「7,6,2,1」
        • 11、「11,6,2,1」
    • 3、「3,1」
      • 8、「8、3、1」
      • 9、「9、3、1」
      • 10、「10、3、1」

次にid:6の子孫を選択に、これを行うだけです

SELECT FROM table WHERE ancestors LIKE "%6,2,1"

先祖の列を最新の状態に保つことは、あなたにとって価値があることよりも厄介かもしれませんが、どのDBでも実行可能なソリューションです。

2

Celkoの手法(ネストセット)はかなり優れています。また、「祖先」フィールドと「子孫」フィールドと「距離」フィールドを持つ隣接テーブルを使用しました(たとえば、直接の子/親の距離は1、孫/祖父母の距離は2など)。

これは維持する必要がありますが、挿入の場合はかなり簡単です:トランザクションを使用してから、直接リンク(親、子、距離= 1)をテーブルに配置し、距離を追加して既存の親と子の選択を無視します(機会があればSQLをプルアップできます)、パフォーマンスのために3つのフィールドのそれぞれにインデックスが必要です。このアプローチがいのは、削除の場合です...基本的に、影響を受けたすべてのアイテムをマークしてから、それらを再構築する必要があります。しかし、これの利点は、任意の非循環グラフを処理できることです。一方、ネストされたセットモデルはストレート階層しか実行できません(たとえば、ルート以外の各アイテムには1つの親しかありません)。

2
Jason S

これは間違いなく実行でき、SQLにとってそれほど複雑ではありません。この質問に答え、mysql手続きコードを使用した実際の例をここに提供しました。

MySQL:特定のノードで葉を見つける方法

ブース:満足したら、回答の1つを承認済みとしてマークする必要があります。

1
guru_florida

SQLはチューリング完全言語ではありません。つまり、この種のループを実行することはできません。 SQLとツリー構造を使用して非常に巧妙なことを実行できますが、任意の深さの階層に対して「階層内に」特定のIDを持つ行を記述する方法は考えられません。

あなたの最善の策は、@ Danが示唆したものに沿ったものです。これは、他のより有能な言語でツリーをただ操作することです。実際には、ループを使用して汎用言語でクエリ文字列を生成できます。クエリは、探している階層の深さを反映する複雑な一連の結合(またはサブクエリ)です。ループや複数のクエリよりも効率的です。

1
Daniel Spiewak

https://stackoverflow.com/questions/27013093/recursive-query-emulation-in-mysql で説明されている「With Emulator」ルーチンを使用しました( https:// stackoverflow .com/users/1726419/yossico )。これまでのところ、非常に良い結果が得られています(パフォーマンスに関して)が、豊富なデータや検索対象の多数の子孫はありません。

1
Big_Al_Tx