2009-08-26 11 views
1

私は、ツリー階層を形成するアイテムを含むデータベーステーブル(sqlite)を持っています。各項目には、idフィールド(それ自身の場合)とparentId(親の場合)があります。アイテムが与えられたら、ルートからアイテムまでチェーン全体を取得する必要があります。いくつのSQLクエリが必要ですか?

のように基本的には擬似コードでアルゴリズムが見えます:カーソルがparentItemがrootItemでない場合、= parentItemと後藤2. カーソルPARENTID

  • によってカーソルのparentItemを取得する項目
  • ある

    1. したがって、各項目に対してSQL SELECTクエリを実行する必要があります。

      1つのSQLクエリだけを実行してチェーン全体のrootItem - > ... - >アイテムを取得することは可能ですか?

  • 答えて

    0

    ANSI標準SQLではなく、そうではありません。まあ、それは厳密には当てはまりません。左外部結合を実行し、可能な最大深度をカバーするのに十分な大きさを置くことができますが、最大深度を拘束してその数の結合を含める場合を除き、常に機能するとは限りません。

    行のセットが十分に小さい(たとえば1000未満)場合は、それらのすべてを取り出して把握するだけです。おそらく、単一の読取りトラバーサルよりも速いでしょう。

    親トラバーサルをバッチすることができます。

    SELECT t1.id id1, t1.parent parent1, 
         t2.id id2, t2.parent parent2, 
         t3.id id3, t3.parent parent3, 
         t4.id id4, t4.parent parent4, 
         t5.id id5, t5.parent parent5 
    FROM mytable t1 
    LEFT OUTER JOIN mytable t2 ON t1.parent = t2.id 
    LEFT OUTER JOIN mytable t3 ON t2.parent = t3.id 
    LEFT OUTER JOIN mytable t4 ON t3.parent = t4.id 
    LEFT OUTER JOIN mytable t5 ON t4.parent = t5.id 
    WHERE t1.id = 1234 
    

    のようなクエリがあり、必要な数に拡張します。最後に検索された親がヌルでない場合は、ツリーの先頭にいないので、クエリを再度実行します。この方法で、1-2往復に減らすことをお勧めします。

    これ以外にも、IDのデータをエンコードする方法を見ることができます。これはお勧めしませんが、各ノードの子供数を100に制限すると、IDが10030711のノードに10 - > 03 - > 07 - > 11のパスがあると言うことができます。もちろん、max IDの長さ)、もちろんそれはハッキーです。

    SQLの階層データには2つの基本モデルがあることにも注意してください。隣接リストと入れ子セット。あなたの方法(かなり一般的です)は隣接関係です。ネストされたセットは本当にこの状況では役に立ちませんし、挿入を行うのは複雑です。

    +0

    残念ながら行の私のセットが合理的に大であっても常に成長しています。 –

    2

    データベース内で階層データを整理する方法はたくさんありますが、データを非階層形式に戻すのが最も簡単で、親レコードと子レコードをプログラムで照合します。

    努力の合計:1クエリ+ 1プログラムでデータセットを渡して階層を作成します。


    代替アプローチ:

    私は限られた成功を収めて、過去にこの方法を使用しました。あなたは次のようにvarchar型(最大)のカラムを使用して、ツリー内の各アイテムのパスを格納することができます:

    ID ParentID Path 
    -- -------- ---- 
    1  null  1/ 
    2  1   1/2/ 
    3  null  3/ 
    4  2   1/2/4/ 
    5  4   1/2/4/5/ 
    6  null  6/ 
    7  5   1/2/4/5/7/ 
    9  5   1/2/4/5/9/ 
    

    その時点から、ID = 5の下のすべてのノードを取得することは非常に簡単です:

    SELECT * 
    FROM table 
    WHERE Path like (SELECT Path FROM Table WHERE ID = 5) + '%' 
    
    +0

    素晴らしいテクニック、私はそれを+1するだろうが、私は数時間の票を欠いている。なぜあなたは** limited ** success thoと言うのですか? –

    +0

    なぜ単にSELECT * FROMテーブルWHERE Path LIKE '%/ 5 /%'; ? –

    +0

    @eyze:確かにうまくいくかもしれません:)しかし、ほとんどすべてのデータベース実装では、データベースはワイルドカード文字で始まるような式のインデックスを使用できません。 SQLiteのドキュメント(http://www.sqlite.org/optoverview.html)を参照してください: "LIKEまたはGLOB演算子で構成された用語はインデックスを制約するために使用されることがあります。 。] LIKEまたはGLOBの右側は、ワイルドカード文字で始まらない文字列リテラルでなければなりません。文字列リテラルではないので、上記のコードでインデックスを使用するかどうかはわかりません。 YMMV。 – Juliet

    0

    テーブル構造を変更できますか?左と右のノードを格納するように見えるのは、親ノードだけを格納するよりも扱いが簡単です。これは、単一の選択が可能なためです。以下のリンクを参照してください:

    http://www.mail-archive.com/[email protected]/msg23867.html

    http://weblogs.asp.net/aghausman/archive/2009/03/16/storing-retrieving-hierarchical-data-in-sql-server-database.aspx(これはSQLServerのですが、彼らは助けるかもしれない図を持っている。)

    関連する問題