15

PostgreSQL で再帰クエリを使用して推移閉包を説明する簡単な例を作成しました。

ただし、再帰クエリには何か問題があります。私はまだ構文に慣れていないので、この要求は完全に初心者かもしれません。そのため、事前にお詫び申し上げます。クエリを実行すると、パスの結果でノード 1 が繰り返されることがわかります。誰かがSQLを微調整する方法を理解するのを手伝ってくれますか?

/*           1
           /   \
          2     3
         / \   /
        4  5  6
       /
      7
     / \
    8   9
*/

create table account(
acct_id INT,
parent_id INT REFERENCES account(acct_id),
acct_name VARCHAR(100),
PRIMARY KEY(acct_id)
);

insert into account (acct_id, parent_id, acct_name) values (1,1,'account 1');
insert into account (acct_id, parent_id, acct_name) values (2,1,'account 2');
insert into account (acct_id, parent_id, acct_name) values (3,1,'account 3');
insert into account (acct_id, parent_id, acct_name) values (4,2,'account 4');
insert into account (acct_id, parent_id, acct_name) values (5,2,'account 5');
insert into account (acct_id, parent_id, acct_name) values (6,3,'account 6');
insert into account (acct_id, parent_id, acct_name) values (7,4,'account 7');
insert into account (acct_id, parent_id, acct_name) values (8,7,'account 8');
insert into account (acct_id, parent_id, acct_name) values (9,7,'account 9');

WITH RECURSIVE search_graph(acct_id, parent_id, depth, path, cycle) AS (
        SELECT g.acct_id, g.parent_id, 1,
          ARRAY[g.acct_id],
          false
        FROM account g
      UNION ALL
        SELECT g.acct_id, g.parent_id, sg.depth + 1,
          path || g.acct_id,
          g.acct_id = ANY(path)
        FROM account g, search_graph sg
        WHERE g.acct_id = sg.parent_id AND NOT cycle
)
SELECT path[1] as Child,parent_id as Parent,path || parent_id as path FROM search_graph
ORDER BY path[1],depth;
4

2 に答える 2

9

いくつかの場所で簡略化できます (acct_idparent_idがであると仮定NOT NULL):

WITH RECURSIVE search_graph AS (
   SELECT parent_id, ARRAY[acct_id] AS path
   FROM   account

   UNION  ALL
   SELECT g.parent_id, sg.path || g.acct_id
   FROM   search_graph sg
   JOIN   account g ON g.acct_id = sg.parent_id 
   WHERE  g.acct_id <> ALL(sg.path)
   )
SELECT path[1] AS child
     , path[array_upper(path,1)] AS parent
     , path
FROM   search_graph
ORDER  BY path;
  • acct_iddepthcycleは、クエリの単なるノイズです。
  • 条件は、最上位ノードからの重複エントリが結果に含まれる前にWHERE、 1 ステップ前に再帰を終了する必要があります。それはあなたの元の「オフバイワン」でした。

残りはフォーマットです。

グラフ内で可能な唯一の円が自己参照であることがわかっている場合は、それを安くすることができます。

WITH RECURSIVE search_graph AS (
   SELECT parent_id, ARRAY[acct_id] AS path, acct_id <> parent_id AS keep_going
   FROM   account

   UNION  ALL
   SELECT g.parent_id, sg.path || g.acct_id, g.acct_id <> g.parent_id
   FROM   search_graph sg
   JOIN   account g ON g.acct_id = sg.parent_id 
   WHERE  sg.keep_going
)
SELECT path[1] AS child
     , path[array_upper(path,1)] AS parent
     , path
FROM   search_graph
ORDER  BY path;

SQL フィドル。

varchar(5)配列の連結は修飾子を失いますが、rCTE は型が正確に一致することを主張するため、修飾子 ( など) を持つデータ型には (少なくとも pg v9.4 までは) 問題があることに注意してください。

于 2014-01-07T22:53:06.737 に答える
0

アカウント 1 を独自の親として設定しています。そのアカウントの親を に設定すると、nullそのアカウントを開始ノードと終了ノードの両方にすることを避けることができます (ロジックをセットアップする方法では、サイクルを含めますが、そのサイクルには追加しません。これは妥当と思われます)。case when parent_id is not null then path || parent_id else path endまた、最後の「パス」列を最後にnullを避けるようなものに変更すると、少し見栄えがよくなります。

于 2014-01-07T20:40:33.413 に答える