外部キー制約の使いどころと ON DELETE オプション
整合性保証と ON DELETE CASCADE / SET NULL / RESTRICT の使い分け。
外部キー制約が守るもの
外部キー (FOREIGN KEY) 制約は、「この列の値は必ず参照先テーブルの主キーに存在する」ことを DB レベルで保証します。アプリ側のバグでデタラメな user_id が入ってしまっても、DB が拒否します。
外部キーを張らない選択肢ももちろんあります(後述)が、そのときは「整合性はアプリで絶対に守る」という約束を永続的に引き受けることになります。開発者が変わってもテストが手薄でも守れるか、と問うと、たいていの場合は DB に任せた方が安全です。
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
total NUMERIC(12,2) NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE RESTRICT
ON UPDATE CASCADE
);ON DELETE: CASCADE / SET NULL / RESTRICT / NO ACTION
親が削除されたときの子の扱いを ON DELETE 句で決めます。
- RESTRICT / NO ACTION: 子がいる親の削除を拒否する(安全側。デフォルトは RDBMS により異なる)
- CASCADE: 親削除時に子も連鎖削除。「親なしでは存在意義がない」子向け(注文と注文明細)
- SET NULL: 子の外部キー列を NULL にする。「親とのつながりは任意」な関係向け(記事とカテゴリ)。列が NOT NULL だと使えない
- SET DEFAULT: デフォルト値にセット。使用頻度は低い
RESTRICT と NO ACTION の違い: NO ACTION はトランザクション末尾に検査を遅延できる(DEFERRABLE 指定時)、RESTRICT は即座に検査。PostgreSQL ではこの差が出る場面があります。
CASCADE は便利だが過信は禁物
ON DELETE CASCADE は連鎖削除してくれて便利ですが、意図しない大量データ削除の入口にもなります。ユーザー 1 人を消したら、その人の注文・レビュー・コメント・ファイルが全部消える、というのが本当に望ましいのかを毎回確認します。
さらに、CASCADE 連鎖中は大量の行ロックが発生し、他トランザクションをブロックします。数百万行の子テーブルで CASCADE 削除すると、本番で長時間のロック待ちを引き起こすことがあります。
代替案: 論理削除(deleted_at)+ 定期バッチでの物理削除に切り替えると、ピーク時間帯のロックを回避できます。
パフォーマンス — インデックスと書き込みコスト
外部キー制約には隠れたコストがあります。
- 親側の PK は必ずインデックス済みだが、子側の FK 列にはインデックスが自動生成されない(MySQL InnoDB は例外で自動生成)。親を削除・更新すると子テーブルをフルスキャンして参照を確認するので、必ず子側 FK 列にインデックスを張る
- 書き込みのたびに参照先存在チェックが走るため、INSERT/UPDATE がわずかに遅くなる
- バルクロード時は外部キー制約を一時的に無効化することもある(PostgreSQL:
ALTER TABLE ... DISABLE TRIGGER ALL、MySQL:SET FOREIGN_KEY_CHECKS = 0)
MySQL InnoDB 固有の注意点
MySQL InnoDB には外部キーまわりの独特な挙動があります。
- FK 列に自動でインデックスを生成する(PostgreSQL / SQL Server はしない)
- 文字列型 FK の場合、文字コード / 照合順序 (collation) が親子で一致しないと FK 作成が失敗する(
utf8mb4_0900_ai_ci等を親子で揃える。数値型 FK には関係しない) ALTER TABLE ... DROP FOREIGN KEYで名前を指定する必要があり、SHOW CREATE TABLEで確認が必要- MyISAM エンジンは FK を無視する(現在は InnoDB 既定なので通常問題にならないが、古いスキーマで遭遇することがある)
外部キーを張らない選択肢
大規模・高スループットな系では、あえて外部キー制約を張らない設計もあります。
- シャーディング / マイクロサービス境界: 参照先が別 DB / 別サービスにあると FK を張れない
- イベントログ / 時系列テーブル: 取り込み速度最優先で、整合性は ETL 後にチェックする
- 書き込みピーク緩和: FK チェックのオーバーヘッドを削って INSERT レートを稼ぐ
いずれの場合も、「整合性をどこで保証するか」を明文化することが必須です。暗黙のうちに FK を外すのが最悪のパターンです。
