PostgreSQL
 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 и рекурсивна част (втората SELECT ) комбинирани от UNION .

О:Присъединяването към таблицата се противопоставя на намирането на дъщерните елементи на node_id .

B:Изграждане на json обекта за детето, което може да бъде вмъкнато в неговия родител

C:Изграждане на пътя, където трябва да се вмъкне дъщерният обект (от root). Прозоречната функция row_number() (https://www.postgresql.org/docs/current/static/tutorial-window.html) генерира индекса на детето в масива children на родителя.

D:Частта с рекурсия работи като начална част с една разлика:не търси основния елемент, а елемента, който има родителския възел на последната рекурсия.

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Защо PostgreSQL не връща нулеви стойности, когато условието е <> вярно

  2. Внедряване на Django в Heroku (грешка Psycopg2)

  3. Имената на часови зони с идентични свойства дават различен резултат, когато се прилагат към времеви печат

  4. Как да се справя с отварянето/затварянето на Db връзка в Go приложение?

  5. PostgreSQL v13 Разгръщане и мащабиране с ClusterControl 1.8.2