7

ID、親、タイトルテーブルとして編成されたカテゴリのツリーを必要とするプロジェクトに取り組んでいます。Postgresでカテゴリとそのサブカテゴリ(およびルートカテゴリが親= 0の場合は完全なツリー)を取得する最良の方法はどれですか? 私は純粋なデータベース ソリューションを探していますが、Ruby と PHP の方法があれば、それも素晴らしいでしょう。

主な目標は select 節の速度です。なぜなら、このテーブルのデータは更新/挿入/削除の速度にとって重要ではないからです。

UPD: パス検索もあります。つまり、現在の頂点 (カテゴリ) からルート カテゴリへのパスです。

4

6 に答える 6

5

カテゴリとそのサブカテゴリを取得する

サブアイテムの深さが限られている場合は、自己結合を使用してこれを行うことができます。2つのレベルの深さ:

SELECT *
FROM categories AS child
LEFT JOIN categories AS parent ON parent.id=child.parent
LEFT JOIN categories AS grandparent ON grandparent.id=parent.parent
WHERE child.id=(id) OR parent.id=(id) OR grandparent.id=(id);

'parent-id-foreign-key'タイプのスキーマに対して標準SQLを使用して、任意の深さの階層に対してこれを行うことはできません。

一部のDBMSは、さまざまな方法でこのようなことを可能にする非標準の階層ツールを提供しますが、DBMS間互換のコードに固執する場合は、階層を表すより優れたモデルの1つにスキーマを再調整する必要があります。2つの人気のあるものは次のとおりです。

  • 入れ子集合。ツリーの深さ優先探索を表す線形順序をターゲットテーブルの2つの列に格納します(ターゲットに明示的な順序がある場合は、そのうちの1つがすでにあります)。

  • 隣接関係。各祖先/子孫のペアを個別の結合テーブルに格納します。

それぞれのアプローチには長所と短所があり、さまざまなタイプの追加/削除/移動位置操作のコストに影響を与える可能性のある多数のバリエーション(たとえば、スパースネストされたセットの番号付け、ARの「距離」)があります。個人的には、ARよりも冗長性が少ないため、デフォルトでは単純化された入れ子集合モデルに引き寄せられる傾向があります。

于 2009-02-25T12:43:42.597 に答える
3

私はltreeをいじってみました。これはPostgreSQLのcontribモジュールであり、スレッド化されたコメントに適しているかどうかを確認します。パスを格納する列をテーブルに作成し、その上にltreeインデックスを作成します。次に、次のようなクエリを実行できます。

 ltreetest=# select path from test where path ~ '*.Astronomy.*';
                     path                      
-----------------------------------------------
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
 Top.Collections.Pictures.Astronomy
 Top.Collections.Pictures.Astronomy.Stars
 Top.Collections.Pictures.Astronomy.Galaxies
 Top.Collections.Pictures.Astronomy.Astronauts

挿入、更新、削除などでどれだけうまく機能するかを判断するのに十分な工夫をしていません。削除は次のようになると思います。

DELETE FROM test WHERE path ~ '*.Astronomy.*';

私が考えているのは、スレッド化されたコメントテーブルは次のようになるかもしれません。

CREATE SEQUENCE comment_id_seq
  INCREMENT 1
  MINVALUE 1
  MAXVALUE 9223372036854775807
  START 78616
  CACHE 1;

CREATE TABLE comments (
comment_id int PRIMARY KEY,
path ltree,
comment text
);

CREATE INDEX comments_path_idx ON comments USING gist (path);

挿入物は大まかに(そしてテストされていない)次のようになります:

CREATE FUNCTION busted_add_comment(text the_comment, int parent_comment_id) RETURNS void AS
$BODY$
DECLARE
    INT _new_comment_id; -- our new comment_id
    TEXT _parent_path;   -- the parent path
BEGIN
    _new_comment_id := nextval('comment_id_seq'::regclass);
    SELECT path INTO _parent_path FROM comments WHERE comment_id = parent_comment_id;

    -- this is probably busted SQL, but you get the idea... this comment's path looks like
    --   the.parent.path.US
    --
    -- eg (if parent_comment_id was 5 and our new comment_id is 43):
    --  3.5.43
    INSERT INTO comments (comment_id, comment, path) VALUES (_new_comment_id, the_comment, CONCAT(_parent_path, '.', _new_comment_id));

END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;

か何か。基本的に、パスはすべての主キーで構成される単なる階層です。

于 2009-02-25T16:44:33.907 に答える
3

「ltree」 contrib モジュールを見てください。

于 2009-02-25T10:33:00.430 に答える
1

私はこの種の状況のた​​めに入れ子集合モデルが好きになりました。更新と挿入は少し注意が必要ですが、選択は通常非常に簡潔で高速です。ノードの親への実際の参照を追加すると、パフォーマンスがさらに向上する可能性があります(場合によっては結合が削除されます。また、子ノードの自然ソートも含まれます。

現在のノードとすべての子に対する一般的なクエリは次のようになります。

select name
from nestedSet c inner join nestedSet p ON c.lft BETWEEN p.lft AND p.rgt
where p.id = 1
order by lft

いくつかの適切に配置されたgroup by句は、ツリーに関するいくつかの簡単な統計も取得します。

于 2009-02-25T17:36:34.453 に答える
0

追加するだけで、MySQL で階層データを管理するという記事には、ツリー操作などの SQL の例を含む、隣接リスト モデルとネストされたセット モデルの適切な説明があります。

RDBMS の階層は難しいトピックです。Joe Celkoの Trees and Hierarchies in SQL for Smartiesは、いつか購入して読みたいリストに入っています。

于 2009-02-26T01:51:05.543 に答える