PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Postgres - Chuyển đổi danh sách kề thành đối tượng JSON lồng nhau

Sử dụng WITH RECURSIVE (https://www.postgresql.org/docs/current/static/queries-with.html) và Hàm JSON (https://www.postgresql.org/docs/current/static/functions-json.html) I xây dựng giải pháp này:

db <> fiddle

Chức năng cốt lõi:

    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

Mỗi WITH RECURSIVE chứa một SELECT bắt đầu và một phần đệ quy (SELECT thứ hai ) được kết hợp bởi một UNION .

A:Việc tham gia bảng sẽ tự tìm được các phần tử con của một node_id .

B:Xây dựng đối tượng json cho đối tượng con có thể được chèn vào đối tượng mẹ của nó

C:Xây dựng đường dẫn mà đối tượng con phải được chèn (từ gốc). Hàm cửa sổ row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html) tạo chỉ mục con trong mảng con của mảng cha.

D:Phần đệ quy hoạt động như phần ban đầu với một điểm khác biệt:Nó không tìm kiếm phần tử gốc mà tìm phần tử có nút cha của phần đệ quy cuối cùng.

E:Thực thi đệ quy và lọc tất cả các phần tử không có phần tử nào cho kết quả này:

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

Mặc dù tôi không tìm thấy cách nào để thêm tất cả các phần tử con trong đệ quy (json gốc không phải là biến toàn cục; vì vậy nó luôn biết những thay đổi của tổ tiên trực tiếp, không phải anh chị em của chúng), tôi đã phải lặp lại các hàng trong một bước giây.

Đó là lý do tại sao tôi xây dựng hàm. Trong đó, tôi có thể thực hiện lặp lại cho một biến toàn cục. Với hàm jsonb_insert Tôi đang chèn tất cả các phần tử được tính toán vào một đối tượng json gốc - bằng cách sử dụng đường dẫn được tính toán.

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;

Bước cuối cùng là gọi hàm và làm cho JSON dễ đọc hơn (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": []
        }]
    }]
}

Tôi chắc chắn rằng có thể tối ưu hóa truy vấn nhưng đối với một bản phác thảo thì nó hoạt động.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Truy vấn JSON lồng nhau PostgreSQL

  2. Cách triển khai PostgreSQL trên DigitalOcean

  3. Cách cài đặt PostgreSQL 12 trên Ubuntu 20.04 / 18.04 / 16.04

  4. Đặc quyền siêu người dùng mặc định của postgres bị xóa vô tình - tôi có thể lấy lại không?

  5. Làm cách nào để thay đổi một bảng PostgreSQL và tạo một cột duy nhất?