PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Requête récursive Postgres avec row_to_json

Désolé pour la réponse très tardive, mais je pense avoir trouvé une solution élégante qui pourrait devenir une réponse acceptée à cette question.

Basé sur le super "petit hack" trouvé par @pozs, j'ai trouvé une solution qui :

  • résout la situation des "vols voyous" avec très peu de code (en tirant parti du NOT EXISTS prédicat)
  • évite tout le calcul de niveau/conditions
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

Mettre à jour :

Testé avec des données très simples, cela fonctionne, mais comme posz l'a souligné dans un commentaire, avec ses exemples de données, certains nœuds feuilles voyous sont oubliés. Mais, j'ai découvert qu'avec des données encore plus complexes, la réponse précédente ne fonctionnait pas non plus, car seuls les nœuds feuilles voyous ayant un ancêtre commun avec des nœuds feuilles "de niveau maximum" sont capturés (quand "1.2.5.8" n'est pas là, " 1.2.4" et "1.2.5" sont absents car ils n'ont pas d'ancêtre commun avec un nœud feuille de "niveau max").

Voici donc une nouvelle proposition, mélangeant le travail de posz avec le mien en extrayant le NOT EXISTS sous-requête et en faire une UNION interne , en tirant parti de UNION capacités de déduplication (en tirant parti des capacités de comparaison 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;

Testé sur http://rextester.com/SMM38494;)