MySQLにおける階層データの再帰クエリ実装

MySQL 8.0以降では、標準SQLに準拠した共通テーブル式(CTE)を利用した再帰クエリの記述が可能です。これにより、組織図やカテゴリ階層のようなツリー構造を持つデータを効率的に取得できるようになります。ここでは、WITH RECURSIVE句を用いた具体的な実装方法を解説します。

データベース構造の定義

例として、組織の部署情報を管理するテーブルを作成します。各レコードは自身の識別子(unit_id)と、上位部署への参照(superior_id)を持ちます。

CREATE TABLE organization_units (
    unit_id INT AUTO_INCREMENT PRIMARY KEY,
    unit_name VARCHAR(100) NOT NULL,
    superior_id INT,
    FOREIGN KEY (superior_id) REFERENCES organization_units(unit_id)
);

検証用のサンプルデータを挿入します。「本社」を頂点とし、その配下に「開発本部」や「営業部」がぶら下がる構造を想定します。

INSERT INTO organization_units (unit_name, superior_id) VALUES 
('本社', NULL),
('開発本部', 1),
('営業部', 1),
('Webアプリ課', 2),
('モバイル課', 2),
('法人営業課', 3);

再帰クエリの実行

「開発本部」を起点とし、その配下に存在するすべての下部組織を取得するクエリを構築します。再帰CTEは、初期結果を定義する「非再帰項」と、自身を参照する「再帰項」で構成されます。

WITH RECURSIVE UnitTree AS (
    -- 1. 基点となるデータ(非再帰項)
    SELECT unit_id, unit_name, superior_id
    FROM organization_units
    WHERE unit_name = '開発本部'
    
    UNION ALL
    
    -- 2. 再帰的にデータを結合する部分(再帰項)
    SELECT child.unit_id, child.unit_name, child.superior_id
    FROM organization_units AS child
    INNER JOIN UnitTree AS parent ON child.superior_id = parent.unit_id
)
SELECT * FROM UnitTree;

クエリの動作原理

このクエリの実行フローは以下の通りです。

  1. 非再帰項の実行: 最初にWHERE句で指定した「開発本部」の行が取得され、UnitTreeという一時結果セットに格納されます。
  2. 再帰項の実行: 前のステップで作成されたUnitTreeと元のテーブルをINNER JOINします。ここでparent.unit_idと一致するchild.superior_idを持つ行(直属の下部組織)が抽出され、再びUnitTreeに追加されます。
  3. 反復と終了: 新しい結果が返されなくなるまでステップ2が繰り返されます。最終的に、蓄積された全ての結果がメインのSELECT文によって出力されます。

タグ: MySQL SQL WITH RECURSIVE Common Table Expression 階層データ

8月1日 19:20 投稿