回答が非常に遅くなって申し訳ありませんが、この質問に対する回答として受け入れられる可能性のあるエレガントな解決策を見つけたと思います。
@pozsによって発見された素晴らしい「小さなハック」に基づいて、私は次のような解決策を思いつきました。
- 「不正な葉」の状況をごくわずかなコードで解決します(
NOT EXISTS
を活用します) 述語) - レベル全体の計算/条件の処理を回避します
WITH RECURSIVE customer_area_tree("id", "customer_id", "parent_id", "name", "description", "children") AS (
-- tree leaves (no matching children)
SELECT c.*, json '[]'
FROM customer_area_node c
WHERE NOT EXISTS(SELECT * FROM customer_area_node AS hypothetic_child WHERE hypothetic_child.parent_id = c.id)
UNION ALL
-- pozs's awesome "little hack"
SELECT (parent).*, json_agg(child) AS "children"
FROM (
SELECT parent, child
FROM customer_area_tree AS child
JOIN customer_area_node parent ON parent.id = child.parent_id
) branch
GROUP BY branch.parent
)
SELECT json_agg(t)
FROM customer_area_tree t
LEFT JOIN customer_area_node AS hypothetic_parent ON(hypothetic_parent.id = t.parent_id)
WHERE hypothetic_parent.id IS NULL
更新 :
非常に単純なデータでテストされた場合、それは機能しますが、poszがコメントで指摘したように、彼のサンプルデータでは、いくつかの不正なリーフノードが忘れられています。しかし、さらに複雑なデータでは、「最大レベル」のリーフノードを持つ共通の祖先を持つ不正なリーフノードのみがキャッチされるため、前の回答も機能しないことがわかりました(「1.2.5.8」がない場合は」 1.2.4"と"1.2.5"は、「最大レベル」のリーフノードと共通の祖先がないため存在しません。
NOT EXISTS
を抽出することで、poszの作業と私の作業を組み合わせた新しい提案があります。 サブリクエストして内部のUNION
、UNION
を活用 重複排除機能(jsonb比較機能を活用):
<!-- language: sql -->
WITH RECURSIVE
c_with_level AS (
SELECT *, 0 as lvl
FROM customer_area_node
WHERE parent_id IS NULL
UNION ALL
SELECT child.*, parent.lvl + 1
FROM customer_area_node child
JOIN c_with_level parent ON parent.id = child.parent_id
),
maxlvl AS (
SELECT max(lvl) maxlvl FROM c_with_level
),
c_tree AS (
SELECT c_with_level.*, jsonb '[]' children
FROM c_with_level, maxlvl
WHERE lvl = maxlvl
UNION
(
SELECT (branch_parent).*, jsonb_agg(branch_child)
FROM (
SELECT branch_parent, branch_child
FROM c_with_level branch_parent
JOIN c_tree branch_child ON branch_child.parent_id = branch_parent.id
) branch
GROUP BY branch.branch_parent
UNION
SELECT c.*, jsonb '[]' children
FROM c_with_level c
WHERE NOT EXISTS (SELECT 1 FROM c_with_level hypothetical_child WHERE hypothetical_child.parent_id = c.id)
)
)
SELECT jsonb_pretty(row_to_json(c_tree)::jsonb)
FROM c_tree
WHERE lvl = 0;
http://rextester.com/SMM38494でテスト済み;)