web-dev-qa-db-ja.com

CTE:1つのステートメントですべての親とすべての子供を取得

このCTEの実例があります。

私はすべての祖父母とすべての子供を選択できます。

しかし、1つのステートメントですべての祖父母とすべての子供を選択するにはどうすればよいですか?

この例では、「Father」を入力として指定すると、祖父、父、息子が出力として必要になります。

PostgreSQLを使用しています。しかし、私はこの質問は標準SQLであるべきだと思います。

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

更新

上記は、単純化された実際の例です。ツリーの深さは最大100レベルです。 「父」より上のいくつかのレベルの祖先と、その下にいくつかのレベルの子孫が存在する可能性があります。すべての祖先とすべての子孫が欲しい。

8
guettli

すべての祖先とすべての子孫が必要な場合は、2つのクエリを1つに結合できます。 2つのCTEを使用してから、単純な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 ;

上記のエラーがない場合は、改善できます。

  • 開始ノードを1つのCTEにのみ配置することにより、最後のUNIONUNION ALLに変更します。
  • 暗黙の結合の代わりにJOIN .. ONを使用します。
  • sELECTとCTEの列リスト間の不一致を修正しました。

クエリは次のようになります。

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ᵀᴹ