Featured image of post PostgreSQL – CTEを使用した再帰クエリ

PostgreSQL – CTEを使用した再帰クエリ

PostgreSQL – CTEを使用した再帰クエリ

厳密に言えば、このプロセスは反復(iteration)であり、再帰(recursion)ではありませんが、RECURSIVE は SQL 標準委員会によって選ばれた用語です。

Postgres 再帰クエリの一般的な構造は次のようになります:

  1. 非再帰的な select ステートメント
  2. Union または union all
  3. 再帰的な select ステートメント
WITH RECURSIVE name_cte AS (
  SELECT statement  /* non-recursive statement */
  UNION [ALL]
  SELECT statement  /*recursive statement referencing the above select statement */
)
SELECT * FROM name_cte;

Postgres 再帰クエリの仕組み

  1. 非再帰的なステートメントを評価し、一時テーブルを作成します
  2. 再帰項を評価し、それらを一時テーブルに追加します
  3. 作業テーブルが空になるまでステップ 2 を繰り返します

Union と union all の違いは、union all は重複を許可するのに対し、union は重複を一切許可しないことです。

最初の 10 個の自然数

WITH RECURSIVE tens AS (
   SELECT 1 as n
 UNION ALL
   SELECT n+1 FROM tens
)
SELECT n FROM tens limit 10;

これは Postgres 再帰クエリの基本的な例で、最初の 10 個の自然数を出力します。

recursive query

自然数の階乗を求める Postgres 再帰クエリ:

WITH RECURSIVE fact (n, factorial)
AS (
    SELECT 1 as n, 5 as factorial
union all
    SELECT n+1, factorial*n FROM fact where n < 5
)
SELECT * FROM fact;

このクエリは 2 つのテーブルを出力します。1 つは最初の 5 つの自然数を含み、もう 1 つのテーブルには階乗を求めるために実行された計算が含まれます。

最後の行だけを出力することもできますが、ここでは反復と計算がどのように行われるかを見ることができます。

recursive query

フィボナッチ数列を出力する Postgres 再帰クエリ:

WITH RECURSIVE fibb
AS (
    SELECT 1::bigint as n, 0::bigint as a, 1::bigint as b
UNION ALL
    SELECT n+1, b as a, (a+b) as b FROM fibb
)
SELECT b FROM fibb limit 10;

これはフィボナッチ数列を 10 まで出力します。

recursive query

組織階層

Postgres 再帰クエリの助けを借りて、組織階層を見つけることができます:

テーブルを作成するには:

INSERT INTO employees (
  employee_id,
  full_name,
  manager_id
)
VALUES
(1, 'Abhi', NULL),
(2, 'Bhargav', 1),
(3, 'Chay', 1),
(4, 'Dravid', 1),
(5, 'Erin', 1),
(6, 'Ford', 2),
(7, 'Gagan', 2),
(8, 'Harry', 3),
(9, 'Isaac', 3),
(10, 'Jack', 4),
(11, 'Kiran', 5);

Abhi はボスであり、最初のレベルになります。Bhargav、Chay、Dravid、Erin は次のレベルになり、残りの人々は最後のレベルになります。

recursive query

クエリ:

WITH RECURSIVE subordinates AS (
  SELECT employee_id, manager_id, full_name, 0 as level
  FROM employees
  WHERE manager_id IS NULL

  UNION ALL

  SELECT e.employee_id, e.manager_id, e.full_name, level+1
  FROM employees e
  INNER JOIN subordinates s ON s.employee_id = e.manager_id
)  
SELECT * FROM subordinates;

出力は次のようになります:

recursive query

Reference

All rights reserved,未經允許不得隨意轉載
Built with Hugo
テーマ StackJimmy によって設計されています。