PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Postgres - Ubah daftar adjacency menjadi objek JSON bersarang

Menggunakan WITH RECURSIVE (https://www.postgresql.org/docs/current/static/queries-with.html) dan Fungsi JSON (https://www.postgresql.org/docs/current/static/functions-json.html) Saya buat solusi ini:

db<>biola

Fungsionalitas inti:

    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

Setiap WITH RECURSIVE berisi SELECT . awal dan bagian rekursi (kedua SELECT ) digabungkan dengan UNION .

A:Menggabungkan tabel dengan dirinya sendiri untuk menemukan turunan dari node_id .

B:Membangun objek json untuk anak yang dapat dimasukkan ke induknya

C:Membangun jalur di mana objek anak harus dimasukkan (dari root). Fungsi jendela row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html) menghasilkan indeks anak dalam larik anak dari induk.

D:Bagian rekursi berfungsi sebagai bagian awal dengan satu perbedaan:Ini tidak mencari elemen root tetapi untuk elemen yang memiliki simpul induk dari rekursi terakhir.

E:Menjalankan rekursi dan memfilter semua elemen tanpa anak memberikan hasil ini:

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": []}

Meskipun saya tidak menemukan cara untuk menambahkan semua elemen anak-anak dalam rekursi (asal json bukan variabel global; jadi selalu tahu perubahan leluhur langsung, bukan saudara kandungnya), saya harus mengulangi baris dalam langkah detik.

Itu sebabnya saya membangun fungsi. Di sana saya bisa melakukan iterasi untuk variabel global. Dengan fungsi jsonb_insert Saya memasukkan semua elemen terhitung ke dalam objek json root - menggunakan jalur terhitung.

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;

Langkah terakhir adalah memanggil fungsi dan membuat JSON lebih mudah dibaca (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": []
        }]
    }]
}

Saya yakin adalah mungkin untuk mengoptimalkan kueri tetapi untuk sketsa itu berfungsi.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL SHOW TABLES Setara (psql)

  2. Bagaimana make_timestamp() Bekerja di PostgreSQL

  3. foreach %dopar% + RPostgreSQL

  4. Bagaimana Anda menemukan jumlah baris untuk semua tabel Anda di Postgres

  5. Apa yang Baru di PgBouncer 1.6