0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

自己参照を採用してみた

Last updated at Posted at 2025-12-21

【SQL】部署マスタの設計で「自己参照」を採用してみた

今回は、DB設計における「階層構造の扱い方」についてです。
先日、社内システムで「部署マスタ」を設計する機会がありました。そこで「自己参照(Self-Referencing)」という設計パターンを採用したところ、非常にスッキリと管理できたので、その知見を共有します。

「組織図やカテゴリ分けのDB設計、どうするのが正解?」と悩んでいる方の参考になれば嬉しいです。


🧐 背景:なぜ「自己参照」を選んだのか?

今回作成するのは「部署マスタ」です。
組織図というのは、「本部 → 事業部 → 課 → 係」のように階層構造になっています。

最初は以下のような「階層ごとにテーブルを分ける設計」も頭をよぎりました。

  • t_divisions (本部テーブル)
  • t_departments (部テーブル)
  • t_sections (課テーブル)

しかし、これだと「組織改編で『室』が増えたらどうする?」「テーブル結合(JOIN)が面倒すぎない?」という問題が発生します。

今回の要件を整理すると、部署のデータ数自体はそこまで多くないことがわかっていました。
そこで、「1つのテーブル内で完結させた方が圧倒的に管理しやすい」と判断し、自己参照を採用しました。


💡 「自己参照(Self-Referencing)」とは?

一言で言うと、「親と子の関係を、1つのテーブルの中で表現する仕組み」のことです。
技術的には、テーブル内のカラムが、同じテーブルの主キー(PK)を参照する外部キー(FK)として設定されている状態を指します。

実際のテーブル設計

イメージしやすいように、実際のテーブル構造(簡略版)を見てみましょう。

テーブル名:t_departments

カラム名 データ型 説明
department_id INT 主キー (PK)
department_name VARCHAR 部署名
parent_department_id INT 親部署ID (自己参照FK)

ポイントは parent_department_id です。ここには、親となる部署の department_id が入ります。一番上の階層(親がいない場合)は NULL にします。

データの入り方(レコード例)

department_id department_name parent_department_id 備考
100 営業本部 NULL ここがトップ
101 第一事業部 100 親は営業本部
102 一課 101 親は第一事業部

これだけで、「営業本部 > 第一事業部 > 一課」というツリー構造が表現できています。


🚀 自己参照を採用するメリット

実際に実装してみて感じたメリットは以下の2点です。

1. 階層の深さが無制限になる

これが最大の強みです。「本部→部→課」の下に、急に「係」や「班」ができても、テーブル構造を変更する必要がありません。単にデータを追加(INSERT)するだけで対応可能です。
仕様変更に強い設計と言えます。

2. テーブルが1つで済む

「本部テーブル」「課テーブル」のように管理対象が分散しません。マスタ管理画面を作る際も、1つのテーブルに対するCRUDを作るだけで済むため、実装工数が削減できます。


⚠️ 知っておくべきデメリット・注意点

採用する際は以下の点に注意が必要です。

1. 階層構造を取得するクエリが少し難しい

「ある部署の配下すべてを取得したい」といった場合、単純な SELECTJOIN では深さがわからず苦労します。
これに対応するには、再帰クエリ(WITH RECURSIVE) という少し高度なSQLを使うのが一般的です。

-- 例: 営業本部(ID:100)配下のすべての組織を取得する再帰クエリ
WITH RECURSIVE DepartmentTree AS (
    SELECT department_id, department_name, parent_department_id
    FROM t_departments
    WHERE department_id = 100 -- 起点
    UNION ALL
    SELECT d.department_id, d.department_name, d.parent_department_id
    FROM t_departments d
    INNER JOIN DepartmentTree dt ON d.parent_department_id = dt.department_id
)
SELECT * FROM DepartmentTree;

2. 無限ループのリスクがある

「Aの親はB、Bの親はA」というデータが入ってしまうと、論理的に循環(ループ)してしまい、プログラムが無限ループに陥る危険性があります。
データの登録・更新時に、アプリ側で「自分の子孫を親に設定していないか」などのチェック処理を入れるのが鉄則です。


📚 その他のユースケース

この「自己参照」パターンは、部署マスタ以外にも以下のような「ツリー構造(木構造)」を持つデータに最適です。

  • ECサイトのカテゴリ(家電 > テレビ > 4Kテレビ)
  • 掲示板のコメント(親コメントへの返信、そのまた返信)
  • ファイルシステム(フォルダの中にフォルダ)
  • 社員管理(誰が誰の上司か)

🎁 まとめ

  • データ量が膨大でなく、階層構造を持つデータなら「自己参照」は非常に有力な選択肢。
  • テーブル構成をシンプルに保てるが、再帰クエリの知識は必要になる。
  • 無限ループにならないよう、アプリ側でのバリデーションは必須。

DB設計は「適材適所」が全てです。要件に合わせて、ベストな形を選んでいきましょう!


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?