はじめに
「世帯」と「取引履歴」のように、一方のレコードが他方のレコードに従属する関係を持つテーブル設計はよくあります。このとき、親にあたるレコード(世帯)を削除したら、子にあたるレコード(その世帯の取引履歴)はどうなるべきでしょうか。この振る舞いを明示的に決めるのが外部キー制約(Foreign Key Constraint) と、その削除時の挙動を指定するON DELETEオプションです。
外部キー制約とは
外部キー制約は、あるテーブルのカラムが、別テーブルの主キー(または一意なキー)を参照していることをデータベースに保証させる仕組みです。これにより、「存在しない世帯IDを持つ取引レコード」のような矛盾したデータが作られることを防げます。この「矛盾したデータを作らせない」という性質を参照整合性(Referential Integrity) と呼びます。
CREATE TABLE transactions (
id SERIAL PRIMARY KEY,
household_id INTEGER NOT NULL REFERENCES households(id),
amount INTEGER NOT NULL
);
上記の例では、transactions.household_idがhouseholds.idを参照する外部キーとして定義されています。存在しないhousehold_idを持つ取引を挿入しようとすると、データベース側がエラーを返して拒否します。
親レコードを削除しようとするとどうなるか
外部キー制約だけを設定した状態(デフォルトのON DELETE NO ACTIONまたはRESTRICT)で、参照されている親レコードを削除しようとすると、データベースはエラーを返して削除を拒否します。子レコードが残ったまま親だけが消えると、参照整合性が壊れてしまうためです。
しかし実際のアプリケーションでは、「世帯を削除するときは、その世帯に紐づく取引もまとめて削除したい」という要件がよくあります。これを実現するのがON DELETEオプションです。
ON DELETEの主な選択肢
| オプション | 挙動 |
|---|---|
NO ACTION / RESTRICT(デフォルト) |
子レコードが存在する限り、親レコードの削除自体を拒否する |
CASCADE |
親レコードの削除と同時に、それを参照している子レコードもすべて自動的に削除する |
SET NULL |
親レコードを削除すると、子レコードの外部キーカラムをNULLにする(カラムがNULL許容である必要がある) |
SET DEFAULT |
親レコードを削除すると、子レコードの外部キーカラムをデフォルト値に設定する |
ON DELETE CASCADEの書き方
CREATE TABLE transactions (
id SERIAL PRIMARY KEY,
household_id INTEGER NOT NULL REFERENCES households(id) ON DELETE CASCADE,
amount INTEGER NOT NULL
);
この設定をしておくと、householdsテーブルから該当の世帯を削除した瞬間、その世帯を参照しているtransactionsの全レコードも同じトランザクション内で自動的に削除されます。トランザクション内で処理されるため、途中で失敗すれば全体がロールバックされ、中途半端な状態でデータが残ることはありません。
実務上の注意点
ON DELETE CASCADEは便利な反面、「1件消したつもりが、意図せず大量の関連データまで消えていた」という事故が起きやすい設定でもあります。特に以下のようなケースでは慎重な検討が必要です。
-
カスケードの連鎖: 子テーブルがさらに孫テーブルから参照されており、そちらにも
CASCADEが設定されている場合、削除の影響範囲が何段にも連鎖する可能性がある - テスト用データのリセット処理との組み合わせ: 開発環境で「世帯を1件削除すればテストデータが全部消える」という設計は、テストデータの初期化スクリプトを書く上ではむしろ好都合に働くこともある(実装がシンプルになる)
- 本番データでの誤操作: 本番環境で親レコードを誤って削除すると、カスケードによって関連データが一括で失われるため、削除操作自体に確認ステップを設ける、事前にバックアップを取るといった運用上の対策と組み合わせるのが望ましい
どのオプションを選ぶかは「そのデータが親なしで存在する意味があるか」で判断するとよいでしょう。取引履歴は世帯があってこそ意味を持つデータなのでCASCADEが自然ですが、例えば「投稿者アカウントが消えても投稿自体は匿名化して残したい」ようなケースではSET NULLが適しています。
まとめ
| 項目 | 内容 |
|---|---|
| 外部キー制約 | 別テーブルの主キーを参照していることをDBに保証させ、参照整合性を守る仕組み |
| デフォルトの挙動 | 子レコードが残っていると親レコードの削除を拒否する |
ON DELETE CASCADE |
親削除と同時に子レコードも自動削除する。同一トランザクションで実行される |
| 注意点 | 削除の影響範囲が想定より広がりやすいため、設計判断は慎重に行う |