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

Postgres-隣接リストをネストされたJSONオブジェクトに変換します

    WITH RECURSIVEを使用する (https://www.postgresql.org/docs/current/static/queries-with.html)およびJSON関数(https://www.postgresql.org/docs/current/static/functions-json.html)Iこのソリューションを構築する:

    db<>フィドル

    コア機能:

        WITH RECURSIVE tree(node_id, ancestor, child, path, json) AS  (
          SELECT 
              t1.node_id, 
              NULL::int, 
              t2.node_id,
              '{children}'::text[] || 
                 (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text,-- C
              jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[])) -- B
          FROM test t1
          LEFT JOIN test t2 ON t1.node_id = t2.parent_node                                   -- A
          WHERE t1.parent_node IS NULL
    
          UNION
    
          SELECT
              t1.node_id, 
              t1.parent_node, 
              t2.node_id,
              tree.path || '{children}' || (row_number() OVER (PARTITION BY t1.node_id ORDER BY t2.node_id) - 1)::text, 
              jsonb_build_object('name', t2.name, 'children', array_to_json(ARRAY[]::int[]))
          FROM test t1
          LEFT JOIN test t2 ON t1.node_id = t2.parent_node
          INNER JOIN tree ON (t1.node_id = tree.child)
          WHERE t1.parent_node = tree.node_id                                                -- D
        )
        SELECT                                                                               -- E
            child as node_id, path, json 
        FROM tree 
        WHERE child IS NOT NULL ORDER BY path
    

    すべてのWITH RECURSIVE 開始SELECTが含まれています および再帰部分(2番目のSELECTUNIONで結合 。

    A:node_idの子を見つけるために、テーブルに再度参加します 。

    B:親に挿入できる子のjsonオブジェクトを作成する

    C:子オブジェクトを(ルートから)挿入する必要があるパスを作成します。ウィンドウ関数row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html)は、親の子配列内に子のインデックスを生成します。

    D:再帰部分は最初の部分として機能しますが、1つの違いがあります。ルート要素を検索するのではなく、最後の再帰の親ノードを持つ要素を検索します。

    E:再帰を実行し、子なしですべての要素をフィルタリングすると、次の結果が得られます:

    node_id   path                      json
    2         children,0                {"name": "node2", "children": []}
    4         children,0,children,0     {"name": "node4", "children": []}
    5         children,0,children,1     {"name": "node5", "children": []}
    6         children,0,children,2     {"name": "node6", "children": []}
    3         children,1                {"name": "node3", "children": []}
    7         children,1,children,0     {"name": "node7", "children": []}
    8         children,1,children,1     {"name": "node8", "children": []}
    

    再帰にすべての子要素を追加する方法は見つかりませんでしたが(元のjsonはグローバル変数ではないため、兄弟ではなく直接の祖先の変更を常に認識しています)、数秒のステップで行を反復処理する必要がありました。

    そのため、関数を作成します。そこで、グローバル変数の反復を行うことができます。関数jsonb_insert 計算されたパスを使用して、すべての計算された要素をルートjsonオブジェクトに挿入しています。

    CREATE OR REPLACE FUNCTION json_tree() RETURNS jsonb AS $$
    DECLARE
        _json_output jsonb;
        _temprow record;
    BEGIN
        SELECT 
            jsonb_build_object('name', name, 'children', array_to_json(ARRAY[]::int[])) 
        INTO _json_output 
        FROM test 
        WHERE parent_node IS NULL;
    
        FOR _temprow IN
            /* Query above */
        LOOP
            SELECT jsonb_insert(_json_output, _temprow.path, _temprow.json) INTO _json_output;
        END LOOP;
    
        RETURN _json_output;
    END;
    $$ LANGUAGE plpgsql;
    

    最後のステップは、関数を呼び出してJSONを読みやすくすることです(jsonb_pretty()

    {
        "name": "node1",
        "children": [{
            "name": "node2",
            "children": [{
                "name": "node4",
                "children": []
            },
            {
                "name": "node5",
                "children": []
            },
            {
                "name": "node6",
                "children": []
            }]
        },
        {
            "name": "node3",
            "children": [{
                "name": "node7",
                "children": []
            },
            {
                "name": "node8",
                "children": []
            }]
        }]
    }
    

    クエリを最適化することは可能だと確信していますが、スケッチの場合は機能します。




    1. PHPmysql挿入日付形式

    2. コマンドラインからLinux上のMySQLデータベースを選択する

    3. make_interval()がPostgreSQLでどのように機能するか

    4. WM_CONCAT関数に相当するSQLServer