sql >> データベース >  >> RDS >> PostgreSQL

row_to_jsonを使用したPostgres再帰クエリ

    回答が非常に遅くなって申し訳ありませんが、この質問に対する回答として受け入れられる可能性のあるエレガントな解決策を見つけたと思います。

    @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の作業と私の作業を組み合わせた新しい提案があります。 サブリクエストして内部のUNIONUNIONを活用 重複排除機能(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でテスト済み;)



    1. SQLの貧弱なストアドプロシージャ実行プランのパフォーマンス-パラメータスニッフィング

    2. postgresqlデータベースに画像を挿入します

    3. Oracleでコンマを使用して数値をフォーマットする方法

    4. PostgreSQLのMIN()関数