PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Рекурсивна заявка на Postgres с row_to_json

Съжалявам за много късния отговор, но мисля, че намерих елегантно решение, което може да се превърне в приет отговор на този въпрос.

Въз основа на страхотния „малък хак“, открит от @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" отсъстват, тъй като нямат общ предшественик с нито един листен възел на "макс. ниво".

Ето едно ново предложение, смесващо работата на posz с моята чрез извличане на NOT EXISTS подзаявка и го превръща във вътрешен UNION , като се използва UNION способности за премахване на дублиране (използване на способности за сравнение на 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. INITCAP() – Преобразуване в начални главни букви в PostgreSQL

  2. Добавяне (натискане) и премахване от JSON масив в PostgreSQL 9.5+

  3. Как ефективно да избера предишната ненулева стойност?

  4. Забравеният оператор на присвояване =и обикновеният :=

  5. Проверка дали postgresql таблица съществува под python (и вероятно Psycopg2)