3

次のような階層構造を格納するテーブルがあるとします。

item_id | hierarchical_id 
--------+-----------------
    1   | ;1;
    2   | ;1;2;
    3   | ;1;2;3;
    4   | ;1;2;4;
    5   | ;1;2;4;5;

ここに格納されている階層は、1 がルート、2 が 1 の子、3 と 4 が 2 の子、5 が 4 の子です。

クエリ

SELECT
  -- the substr is used to remove the first and last semicolumns
  regexp_split_to_table(substr(hierarchical_id, 2, length(hierarchical_id) - 2)
                        , E';'
  ) as parent_id,
  item_id,
  hierarchical_id
FROM 
  table

戻り値

parent_id | item_id | hierarchical_id
----------+---------+-----------------
       1  |    1    | ;1;
       1  |    2    | ;1;2;
       2  |    2    | ;1;2;
       1  |    3    | ;1;2;3;
       3  |    3    | ;1;2;3;
       1  |    4    | ;1;2;3;
       2  |    4    | ;1;2;4;
       4  |    4    | ;1;2;4;
       1  |    5    | ;1;2;4;5;
       2  |    5    | ;1;2;4;5;
       4  |    5    | ;1;2;4;5;
       5  |    5    | ;1;2;4;5;

次のような4番目の列を取得するようにクエリを変更するにはどうすればよいですか:

parent_id | item_id | hierarchical_id | distance
----------+---------+-----------------+---------
       1  |    1    | ;1;             | 0
       1  |    2    | ;1;2;           | 1
       2  |    2    | ;1;2;           | 0
       1  |    3    | ;1;2;3;         | 2 
       2  |    3    | ;1;2;3;         | 1
       3  |    3    | ;1;2;3;         | 0
       1  |    4    | ;1;2;4;         | 2
       2  |    4    | ;1;2;4;         | 1
       4  |    4    | ;1;2;4;         | 0
       1  |    5    | ;1;2;4;5;       | 3
       2  |    5    | ;1;2;4;5;       | 2
       4  |    5    | ;1;2;4;5;       | 1
       5  |    5    | ;1;2;4;5;       | 0

の意味は、現在の行のと のdistance間の距離です。例: ノードとそれ自体の間の距離は 0、ノードとその親の間の距離は 1、ノードとその親の間の距離は 2 などです。0 から開始する必要はありません。item_idparent_id

row_numberitem_idの IDhierarchical_idが順序付けられているため、等しい s の各グループに対して 0 で再起動することができれば問題なく動作します。

助言がありますか?

4

2 に答える 2

4

ウィンドウ関数は、多くの制御を提供します。4.2.8を参照してください。ウィンドウ関数呼び出し.

必要な鍵は次のとおりです。

row_number() OVER (PARTITON BY item_id ORDER BY hierarchical_id)

与えられたデータ:

create table t ( item_id integer, hierarchical_id text );
insert into t (item_id, hierarchical_id) values
(1,';1;'),
(2,';1;2;'),
(3,';1;2;3;'),
(4,';1;2;4;'),
(5,';1;2;4;5;');

クエリ:

WITH x AS (
  SELECT regexp_split_to_table(substr(hierarchical_id, 2, length(hierarchical_id) - 2), E';') as parent_id,
    item_id,
    hierarchical_id
  FROM t
)
SELECT 
  *,
  row_number() OVER (PARTITION BY item_id ORDER BY parent_id DESC) - 1 AS distance
FROM x
ORDER BY item_id, parent_id;

生成:

 parent_id | item_id | hierarchical_id | distance 
-----------+---------+-----------------+----------
 1         |       1 | ;1;             |        0
 1         |       2 | ;1;2;           |        1
 2         |       2 | ;1;2;           |        0
 1         |       3 | ;1;2;3;         |        2
 2         |       3 | ;1;2;3;         |        1
 3         |       3 | ;1;2;3;         |        0
 1         |       4 | ;1;2;4;         |        2
 2         |       4 | ;1;2;4;         |        1
 4         |       4 | ;1;2;4;         |        0
 1         |       5 | ;1;2;4;5;       |        3
 2         |       5 | ;1;2;4;5;       |        2
 4         |       5 | ;1;2;4;5;       |        1
 5         |       5 | ;1;2;4;5;       |        0

これは大まかに正しいように見えますが、予想される出力が、実行時に提供したクエリの出力と一致しないように見えるため (Pg 9.1)、確実に知ることは困難です。

于 2012-10-11T08:19:18.837 に答える
1

質問がより洗練されたので、結果だけでなく意図をよりよく表現する定式化を次に示します。

CREATE EXTENSION intarray;

SELECT 
  exploded.*, 
  array_length(h_arr,1) - idx(h_arr,parent_id) AS distance
FROM (
  SELECT unnest(h_arr) AS parent_id, item_id, h_arr 
  FROM (
    SELECT
      item_id,
      regexp_split_to_array( trim(hierarchical_id,';'),';')::int[] as h_arr
    FROM t
  ) h_as_intarray
) exploded;

...すべてのパスが必要なため、少し遅くなりますが。hierarchical_id最初に整数配列として格納された場合、おそらく次のようになります。

ALTER TABLE t ALTER COLUMN hierarchical_id TYPE int[] 
USING (regexp_split_to_array( trim(hierarchical_id,';'),';')::int[]);

恐ろしい正規表現文字列処理をすべて取り除くため、より優れたクエリが得られます。

SELECT 
  exploded.*,
  array_length(hierarchical_id,1) - idx(hierarchical_id,parent_id)  AS distance
FROM (
  SELECT unnest(hierarchical_id) AS parent_id, item_id, hierarchical_id 
  FROM t
) exploded;

...ボーナスポイントとして、この小さなデータセットでも3倍高速であり、より大きなデータセットでそのリードを維持または拡張する可能性があります.

于 2012-10-11T09:04:36.353 に答える