顧客の非バイナリツリーがあり、指定されたノードのツリー内のすべてのIDを取得する必要があります。
テーブルは非常にシンプルで、親IDと子IDを持つ結合テーブルです。これは、私が自分のデータベースに格納したツリーの表現です。
この例では、ノード17を検索すると、14-17が必要になります。 11を検索すると、1-6-5-4-8-11-12-7-2-10-3が必要になります。
順序は重要ではありません。子をノードに追加するときの循環を避けるためにIDだけが必要です。
このクエリを作成しました。祖先の部分は正常に機能し、すべての親ノードを取得しますが、子孫の場合は問題があります。ツリーの一部しか取得できません。たとえば、ノード11では4-10-6-11-7-8を取得しているため、ツリーのすべての右側の部分がありません。
WITH RECURSIVE
-- starting node(s)
starting (parent, child) AS
(
SELECT t.parent, t.child
FROM public.customerincustomer AS t
WHERE t.child = :node or t.parent = :node
)
,
ancestors (parent, child) AS
(
SELECT t.parent, t.child
FROM public.customerincustomer AS t
WHERE t.parent IN (SELECT parent FROM starting)
UNION ALL
SELECT t.parent, t.child
FROM public.customerincustomer AS t JOIN ancestors AS a ON t.child = a.parent
),
descendants (parent, child) AS
(
SELECT t.parent, t.child
FROM public.customerincustomer AS t
WHERE t.parent IN (SELECT parent FROM starting) or t.child in (select child from starting)
UNION ALL
SELECT t.parent, t.child
FROM public.customerincustomer AS t JOIN ancestors AS a ON t.parent = a.child
)
table ancestors
union all
table descendants
ツリーテーブルに含まれている多くの例も、フォームのルート(root_id、null)です。
私の場合、このレコードはありません。
たとえば、最小のツリー14-> 17を例にとると、テーブルには親、子が1つしかない
14 17
非常に原始的な実装:
基本的に、問題を2つのサブ問題に分割します。
ancestors
結果セットに複数のノードがある場合があり、ここで重複が発生する可能性があるため、UNION
(UNION ALL
ではなく)を使用してそれらを削除します。クエリ:
WITH RECURSIVE
ancestors (parent) AS
(
SELECT :node -- start with the given node
UNION ALL
SELECT t.parent -- and find all its ancestors
FROM public.customerincustomer AS t JOIN ancestors AS a ON t.child = a.parent
),
descendants (customer) AS
(
SELECT parent AS customer -- now start with all the ancestors
FROM ancestors
UNION
SELECT t.child -- and find all their descendants
FROM public.customerincustomer AS t JOIN descendants AS d ON t.parent = d.customer
)
SELECT customer
FROM descendants ;
この関数は、node_id
の親レベルを返します。
親行の行(id、null)がないため、「レベル」行があります。
CREATE FUNCTION get_parent(node_id int)
RETURNS integer AS
$$
WITH RECURSIVE get_parent AS
(
SELECT
t1.id,
t1.parent_id,
t1.name,
0 AS level
FROM
tree t1
WHERE
t1.id = node_id
UNION ALL
SELECT
t2.id,
t2.parent_id,
t2.name,
level+1
FROM
tree t2
INNER JOIN
get_parent ON get_parent.parent_id = t2.id
)
SELECT
id
FROM
get_parent
ORDER BY
level DESC
LIMIT 1 ;
$$
LANGUAGE SQL;
get_parent(7);を選択します。
| get_parent | | ---------:| | 6 |
現在、次のクエリは、親ノードに基づいてツリー構造全体を返します。
WITH RECURSIVE childs AS
(
SELECT
t1.id,
t1.parent_id,
t1.name
FROM
tree t1
WHERE
t1.id = get_parent(7)
UNION ALL
SELECT
t2.id,
t2.parent_id,
t2.name
FROM
tree t2
INNER JOIN
childs ON childs.id = t2.parent_id
)
SELECT
id,
parent_id,
name
FROM
childs;
id | parent_id |名前 -:| --------:| :------ 6 | 1 | Node 6 4 | 6 | Node 4 8 | 6 | Node 8 11 | 6 | Node 11 7 | 11 | Node 7 10 | 7 | Node 10
db <> fiddle ここ