FOREIGN KEY制約の設定
このレッスンで分かること
- FK の書き方は 3 通り (列レベル / テーブルレベル /
ALTER TABLEで後付け)- 制約名は
CONSTRAINT fk_子_親で明示すると後の運用が楽- 親テーブルが先、子テーブルが後、というテーブル作成順序が鉄則
FOREIGN KEY制約の設定 とは
テーブル間の関連性を設定するFOREIGN KEY制約について解説します。本レッスンでは、FOREIGN KEY制約の設定 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ重要か
前回は外部キーの「考え方」を学びましたが、実務では DDL (CREATE TABLE / ALTER TABLE) で実際に FK を書き下せないと意味がありません。書き方には複数のスタイルがあり、それぞれメリットがあります。さらに「テーブル作成時に同時に書く」場合と「後から ALTER TABLE で追加する」場合があり、どちらも頻出パターンなので両方使えるようになりましょう。
3 つの書き方
外部キーの書き方は 3 通りあります。
SQL クエリ
-- 1) カラム定義の中で書く (列レベル)
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT REFERENCES customers(id)
);
-- 2) テーブル末尾でまとめて書く (テーブルレベル)
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
FOREIGN KEY (customer_id) REFERENCES customers(id)
);
-- 3) ALTER TABLE で後から追加
ALTER TABLE orders
ADD CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id) REFERENCES customers(id);書き方 1 は最短で書けて分かりやすい一方、制約名を自分で付けられず DB が自動命名します。
書き方 2 は複数カラムの複合外部キーにも対応でき、テーブル末尾に制約をまとめられるので可読性が高くなります。
書き方 3 は既存テーブルに後から追加するときの手段で、運用中の DB に変更を加える場面で使います。
| 書き方 | メリット | デメリット |
|---|---|---|
| 列レベル | 最短、分かりやすい | 制約名は自動命名 |
| テーブルレベル | 複合 FK 可、可読性高 | 少し冗長 |
ALTER TABLE | 後付け可、運用中の DB に有効 | 既存データに不整合があると失敗 |
制約名を付ける利点
SQL クエリ
CREATE TABLE orders (
id INT PRIMARY KEY,
customer_id INT,
CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id) REFERENCES customers(id)
);CONSTRAINT fk_orders_customer のように 明示的な名前 を付けておくと、後で ALTER TABLE orders DROP CONSTRAINT fk_orders_customer; のように削除や変更が容易になります。命名規則は fk_{子テーブル}_{親テーブル} が一般的です。
DDL の流れを図で
親テーブルが存在しないと子テーブルの FK 宣言で失敗します。テーブル作成順序は「親が先」が鉄則です。
複合外部キー
複数カラムをまとめて参照する場合は、テーブルレベル記法しか使えません。
SQL クエリ
CREATE TABLE order_items (
order_id INT,
line_no INT,
product_id INT,
PRIMARY KEY (order_id, line_no),
FOREIGN KEY (order_id) REFERENCES orders(id)
);外部キーは「子テーブルに張る」「親側は PRIMARY KEY か UNIQUE」「親テーブルが先に存在する」の 3 点を必ず押さえる。
FK を後から ALTER TABLE で追加するとき、既存データに不整合があると追加自体が失敗します。先にデータをクレンジングしてから FK を追加するのが安全です。
DROP / ALTER で外す
後で FK を外したい、付け替えたいときは制約名を使って操作します。
SQL クエリ
-- 既存制約の削除
ALTER TABLE orders
DROP CONSTRAINT fk_orders_customer;
-- 削除して付け直す (CASCADE オプションを変えたい等)
ALTER TABLE orders
DROP CONSTRAINT fk_orders_customer;
ALTER TABLE orders
ADD CONSTRAINT fk_orders_customer
FOREIGN KEY (customer_id) REFERENCES customers(id)
ON DELETE CASCADE;制約名を付けていなかった場合、自動命名された名前 (PostgreSQL なら orders_customer_id_fkey のような形式) を \d orders などで調べてから DROP します。最初から名前を付けておく方が遥かに楽です。
アンチパターン
- カラム型を親と子で揃え忘れる (INT と BIGINT の混在で意図しないキャストが発生)
- 親テーブルの参照先カラムに UNIQUE 制約が無く、複数の親候補が存在してしまう
- 一括 INSERT で FK を活かしたまま大量ロードし、データ投入が極端に遅くなる
- 制約名を付けず、後で操作対象を特定できない
どれも実務で頻発するハマりポイントです。最初の設計時に潰しておきましょう。
ここまでの要点 FK は 3 通りの書き方、制約名は明示するのが正解。親 → 子の順で CREATE、複合 FK はテーブルレベル記法。型ミスマッチ・UNIQUE 漏れ・制約名なしの 3 大アンチパターンに注意。
まとめ
- FK 宣言は列レベル / テーブルレベル / ALTER TABLE の 3 通り
- 制約名を
CONSTRAINT fk_xxxで明示すると後の運用が楽になる - 親が先、子が後、というテーブル作成順序は守る
- 複合 FK はテーブルレベル記法で書く
- 型ミスマッチや UNIQUE 漏れなどのアンチパターンに注意
次のレッスン
次は ON DELETE/UPDATE ポリシー です。SQLにおける外部キー制約、ON DELETE/UPDATEポリシーについてSQLで実践的に設定・確認します。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- FK 制約の設定 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. FK 制約の設定 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE departments (
id INT PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE employees (
id INT PRIMARY KEY,
name TEXT NOT NULL,
department_id INT,
CONSTRAINT fk_employees_department
FOREIGN KEY (department_id) REFERENCES departments(id)
);
INSERT INTO departments (id, name) VALUES
(1, '営業'),
(2, '開発'),
(3, '人事');
INSERT INTO employees (id, name, department_id) VALUES
(101, '田中', 1),
(102, '佐藤', 2),
(103, '鈴木', 2),
(104, '高橋', 3);
期待される出力
| department | member_count |
|---|---|
| 開発 | 2 |
| 人事 | 1 |
| 営業 | 1 |