web-dev-qa-db-fra.com

CTE: Obtenez tous les parents et tous les enfants dans une seule déclaration

J'ai cet exemple de travail CTE.

Je peux sélectionner tous les grands-parents et tous les enfants.

Mais comment puis-je sélectionner tous les grands-parents et tous les enfants dans une seule déclaration?

Dans cet exemple, je veux grand-père, père, fils comme sortie si je donne "Père" comme entrée.

J'utilise PostgreSQL. Mais je pense que cette question devrait être SQL standard.

Veuillez me corriger si j'utilise la syntaxe spécifique à PostgreSQL.

DROP table if exists tree;

CREATE TABLE tree (
 id SERIAL PRIMARY KEY,
 name character varying(64) NOT NULL,
 parent_id integer REFERENCES tree NULL
);

insert into tree values (1, 'Grandfather', NULL);
insert into tree values (2, 'Father', 1);
insert into tree values (3, 'Son', 2);


-- -------------------------------------
-- Getting all children works  

WITH RECURSIVE rec (id) as
(
  SELECT tree.id, tree.name from tree where name='Father'

  UNION ALL

  SELECT tree.id, tree.name from rec, tree where tree.parent_id = rec.id



  )
SELECT *
FROM rec;

-- Result: 
--  id |  name  
-- ----+--------
--   2 | Father
--   3 | Son



-- -------------------------------------
-- Getting all parents works

WITH RECURSIVE rec (id) as
(
  SELECT tree.id, tree.name, tree.parent_id from tree where name='Father'

  UNION ALL

  SELECT tree.id, tree.name, tree.parent_id from rec, tree where tree.id = rec.parent_id
  )
SELECT id, name
FROM rec;

-- Result
-- id |    name     
-- ----+-------------
--  2 | Father
--  1 | Grandfather

Mise à jour

Ci-dessus est un exemple de travail simplifié. L'arbre peut atteindre 100 niveaux de profondeur. Il peut y avoir plusieurs niveaux d'ancêtres au-dessus de "Père" et plusieurs niveaux de descendants en dessous. Je veux tous les ancêtres et tous les descendants.

8
guettli

Si vous voulez tous les ancêtres et tous les descendants, vous pouvez combiner les deux requêtes en une seule. Utilisez les deux CTE puis un simple UNION:

WITH RECURSIVE 
    -- descendants 
    rec_d (id, name) AS
    (
      SELECT tree.id, tree.name FROM tree WHERE name = 'Father'
      UNION ALL
      SELECT tree.id, tree.name FROM rec_d, tree where tree.parent_id = rec_d.id
    ),
    --  ancestors
    rec_a (id, name, parent_id) AS
    (
      SELECT tree.id, tree.name, tree.parent_id FROM tree WHERE name = 'Father'
      UNION ALL
      SELECT tree.id, tree.name, tree.parent_id FROM rec_a, tree WHERE tree.id = rec_a.parent_id
    )
SELECT id, name FROM rec_a
UNION 
SELECT id, name FROM rec_d ;

Si nous n'avons pas eu d'erreurs ci-dessus, nous pouvons les améliorer:

  • changez le UNION final en UNION ALL en plaçant le ou les nœuds de départ dans un seul des CTE.
  • utilisation JOIN .. ON au lieu des jointures implicites.
  • correction des disparités entre les listes de colonnes SELECT et CTE.

La requête devient:

WITH RECURSIVE 
    -- starting node(s)
    starting (id, name, parent_id) AS
    (
      SELECT t.id, t.name, t.parent_id
      FROM tree AS t
      WHERE t.name = 'Father'          -- this can be arbitrary
    ),
    descendants (id, name, parent_id) AS
    (
      SELECT s.id, s.name, s.parent_id 
      FROM starting AS s
      UNION ALL
      SELECT t.id, t.name, t.parent_id 
      FROM tree AS t JOIN descendants AS d ON t.parent_id = d.id
    ),
    ancestors (id, name, parent_id) AS
    (
      SELECT t.id, t.name, t.parent_id 
      FROM tree AS t 
      WHERE t.id IN (SELECT parent_id FROM starting)
      UNION ALL
      SELECT t.id, t.name, t.parent_id 
      FROM tree AS t JOIN ancestors AS a ON t.id = a.parent_id
    )
TABLE ancestors
UNION ALL
TABLE descendants ;
13
ypercubeᵀᴹ