CTE: Holen Sie sich alle Eltern und alle Kinder in einer Aussage

10

Ich habe dieses funktionierende CTE-Beispiel.

Ich kann alle Großeltern und alle Kinder auswählen.

Aber wie kann ich alle Großeltern und alle Kinder in einer Aussage auswählen?

In diesem Beispiel möchte ich Großvater, Vater, Sohn als Ausgabe, wenn ich "Vater" als Eingabe gebe.

Ich benutze PostgreSQL. Aber ich denke, diese Frage sollte Standard-SQL sein.

Bitte korrigieren Sie mich, wenn ich PostgreSQL-spezifische Syntax verwende.

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

Aktualisieren

Oben ist ein vereinfachtes Arbeitsbeispiel. Der Baum kann bis zu 100 Level tief sein. Es kann mehrere Ebenen von Vorfahren über "Vater" und mehrere Ebenen von Nachkommen unter "Vater" geben. Ich möchte alle Vorfahren und alle Nachkommen.

guettli
quelle
@dezso das ist nur ein Beispiel. Der Baum kann bis zu 100 Level tief sein.
Guettli

Antworten:

15

Wenn Sie alle Vorfahren und alle Nachkommen möchten, können Sie die beiden Abfragen in einer kombinieren. Verwenden Sie die beiden CTEs und dann eine einfache 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 ;

Wenn wir oben keine Fehler haben, können wir sie verbessern:

  • Ändern Sie das Finale UNIONin, UNION ALLindem Sie den / die Startknoten nur in einen der CTEs einfügen.
  • Verwenden Sie JOIN .. ONanstelle der impliziten Verknüpfungen.
  • Behebung von Nichtübereinstimmungen zwischen SELECT- und CTE-Spaltenlisten.

Die Abfrage lautet:

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 ;
ypercubeᵀᴹ
quelle