0Pricing
SQL Academy · レッスン

カテゴリーツリーをたどる

親子関係のツリーを完全に展開します。

「カテゴリーツリーをたどる」はCoddyKit上の無料SQL Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはSQL Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 SQL Academyコースには全4レッスンが含まれています。

カテゴリツリーとは

現実の多くのデータセットには、親子関係があります。商品カタログには、電子機器 → 携帯電話 → スマートフォンのようなカテゴリがあるでしょう。各ノードが親を持つことで、ツリー構造が形成されます。

SQLでは通常、自己参照テーブルとして保存します。各行にidと、同じテーブル内の別の行を指すparent_idがあります。

CREATE TABLE categories (
  id       INT PRIMARY KEY,
  name     VARCHAR(100) NOT NULL,
  parent_id INT REFERENCES categories(id)
);

カテゴリデータの例

小さなカテゴリツリーを登録してみましょう。ルートノードには親がないため、parent_id = NULLになります。それ以外のすべてのノードは、NULLではないparent_idによって親を指します。

INSERT INTO categories (id, name, parent_id) VALUES
  (1, 'Electronics',   NULL),
  (2, 'Phones',         1),
  (3, 'Laptops',        1),
  (4, 'Smartphones',    2),
  (5, 'Feature Phones', 2),
  (6, 'Gaming Laptops', 3),
  (7, 'Ultrabooks',     3);

単純なクエリの問題点

通常のSELECTでは、一度に1つのレベルしか取得できません。3レベルの深さまで到達するには、3つの別々のクエリまたは3つの自己結合が必要になり、ツリーが大きくなるにつれて管理できなくなります。

WITH RECURSIVEを使うと、クエリが自身の出力を参照できるため、この問題を解決できます。新しい行が見つからなくなるまで、レベルごとにたどっていきます。

-- This only shows direct children of Electronics (level 1)
SELECT id, name
FROM   categories
WHERE  parent_id = 1;

WITH RECURSIVE の構成

再帰CTEは、UNION ALLで区切られた2つの部分で構成されます。

1. アンカー部 — 開始行を提供する通常のSELECTです。

2. 再帰部 — CTE自身を結合し、反復ごとに次の階層を生成するSELECTです。

エンジンは、再帰部が0行を返すまで再帰部を繰り返し実行します。

WITH RECURSIVE cte AS (
  -- Anchor: starting rows
  SELECT ...
  UNION ALL
  -- Recursive: join cte to base table
  SELECT ... FROM base_table JOIN cte ON ...
)
SELECT * FROM cte;

ルートからツリー全体をたどる

ルート(parent_id IS NULLの行)から開始し、すべての子孫をたどります。再帰部では、蓄積された各行を親子関係に基づいてcategoriesへ再び結合します。

WITH RECURSIVE category_tree AS (
  -- Anchor: root nodes
  SELECT id, name, parent_id, 1 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  -- Recursive: children of current level
  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories      c
  JOIN   category_tree   ct ON ct.id = c.parent_id
)
SELECT id, name, depth
FROM   category_tree
ORDER  BY depth, id;

パスを追跡する

ルートから各ノードまでの完全なパスを記録すると便利です。再帰を深く進めながら祖先の名前を連結することで、path文字列を作成できます。

これにより、電子機器 / 電話 / スマートフォンのようなパンくずリストを簡単に表示できます。

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id,
         name AS path
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         ct.path || ' / ' || c.name
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT id, name, path
FROM   category_tree
ORDER  BY path;

特定のノードから開始する

ルートから開始する必要はありません。アンカー部のWHERE句を変更すれば、任意のノードのサブツリーをたどれます。ここではPhones(id = 2)から開始し、そのすべての子孫を取得します。

WITH RECURSIVE subtree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  id = 2          -- start at Phones

  UNION ALL

  SELECT c.id, c.name, c.parent_id, s.depth + 1
  FROM   categories c
  JOIN   subtree    s ON s.id = c.parent_id
)
SELECT id, name, depth
FROM   subtree
ORDER  BY depth, id;

上方向にたどる:すべての祖先を見つける

ツリーは逆方向に、つまり葉からルートに向かって上方向へたどることもできます。結合の向きを反転し、下方向ではなくparent_idを上方向にたどるだけです。既知の葉ノードについて完全なパンくずリストが必要な場合に便利です。

WITH RECURSIVE ancestors AS (
  SELECT id, name, parent_id
  FROM   categories
  WHERE  id = 4          -- start at Smartphones

  UNION ALL

  SELECT c.id, c.name, c.parent_id
  FROM   categories c
  JOIN   ancestors  a ON a.parent_id = c.id
)
SELECT id, name
FROM   ancestors
ORDER  BY id;

インデント付きの表示を追加する

子ノードを視覚的にインデントするのは、よく使われるUIパターンです。REPEAT(またはLPAD)をdepth列と組み合わせ、各名前の前にスペースを付けることで、テキストベースのツリー表示を作成できます。

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  REPEAT('    ', depth) || name AS indented_name,
  depth
FROM   category_tree
ORDER  BY path;

無限ループを防ぐ

データに循環(AがBの親で、BがAの親)が含まれていると、再帰が永遠に続いてクラッシュします。訪問済みIDを配列で追跡し、現在のIDがすでに含まれている場合に停止することで、この問題を防げます。

WITH RECURSIVE safe_tree AS (
  SELECT id, name, parent_id,
         ARRAY[id] AS visited
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id,
         st.visited || c.id
  FROM   categories c
  JOIN   safe_tree  st ON st.id = c.parent_id
  WHERE  c.id <> ALL(st.visited)   -- stop if already seen
)
SELECT id, name FROM safe_tree;

ノードごとの子孫数を数える

ツリー全体を取得できたら、集計できます。ここでは、子行を祖先の一覧に対してグループ化することで、各ノードの子孫数を数えます。ナビゲーションメニューでカテゴリ名の横に項目数を表示する場合などに便利です。

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, id AS root_id
  FROM   categories
  WHERE  parent_id IS NULL

  UNION ALL

  SELECT c.id, c.name, c.parent_id, ct.root_id
  FROM   categories    c
  JOIN   category_tree ct ON ct.id = c.parent_id
)
SELECT
  root_id,
  COUNT(*) - 1 AS descendant_count
FROM   category_tree
GROUP  BY root_id
ORDER  BY root_id;

理解度チェック

カテゴリツリーに対する再帰クエリの理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、WITH RECURSIVEを使用して自己参照するカテゴリテーブルをたどる方法を学びました。

重要なポイント:

- アンカー部は開始ノード(通常はルート)を選択します。

- 再帰部はCTEを基底テーブルに再び結合し、次の階層を見つけます。

- depth列を追加して、各ノードが何階層目にあるかを追跡します。

- path文字列を作成して、パンくずリストを生成します。

- parent_idを逆方向にたどって上方向に進み、すべての祖先を見つけます。

- visited配列を使用して、不完全なデータに含まれる循環から保護します。

よくある質問

「カテゴリーツリーをたどる」レッスンは無料ですか?

はい。「カテゴリーツリーをたどる」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、SQL Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 SQL Academyコースには全4レッスンが含まれています。

「カテゴリーツリーをたどる」で何を学びますか?

親子関係のツリーを完全に展開します。 ブラウザで直接実行するハンズオンコードでSQL Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

SQL Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのSQL Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「カテゴリーツリーをたどる」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このSQL Academyレッスンでコードを書いて実行できますか?

はい。すべてのSQL Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 再帰CTEの仕組み
  2. カテゴリーツリーをたどる
  3. 系列とシーケンスを生成する
  4. 無限ループを避ける
← SQL Academyに戻る