[MariaDB]外部キー制約
公開日:2026-01-13
更新日:2026-01-13
更新日:2026-01-13
1. 概要
外部キー制約を使用すると、関連するテーブル間のデータの整合性を保つことできます。
例えば、親子関係があるテーブルがあった場合、子レコードがある時に親レコードを削除すると、エラーが発生して、子レコードだけが存在する状態を防ぐことができます。
ちなみに、deleted_at や is_deleted などのカラムを使って論理削除を採用している場合は、外部キー制約を付けても効果がない場合があるので、よく検討してください。
例えば、親子関係があるテーブルがあった場合、子レコードがある時に親レコードを削除すると、エラーが発生して、子レコードだけが存在する状態を防ぐことができます。
ちなみに、deleted_at や is_deleted などのカラムを使って論理削除を採用している場合は、外部キー制約を付けても効果がない場合があるので、よく検討してください。
2. 動作確認
カテゴリーテーブル(categories)と記事テーブル(articles)を使用して確認します。
記事にカテゴリーIDを持つため、カテゴリーが親です。
記事にカテゴリーIDを持つため、カテゴリーが親です。
2.1 カテゴリーテーブルの作成
SQL
CREATE DATABASE test_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
INSERT INTO categories (id, name) VALUES (1, 'カテゴリー1'), (2, 'カテゴリー2'), (3, 'カテゴリー3');2.2 ON DELETE RESTRICT
子レコードがある時に、親レコードの削除を禁止します。削除しようとするとエラーが発生します。
例えば、注文データと注文明細データのように、子レコードだけ存在しても意味がない場合に使用します。
次の SQL を実行すると、エラーが発生します。
子レコードを削除してから親レコードを削除する場合は、削除できます。
また、存在しない親の ID を指定して INSERT する場合もエラーになります。
例えば、注文データと注文明細データのように、子レコードだけ存在しても意味がない場合に使用します。
SQL
CREATE TABLE articles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
category_id INT NULL,
CONSTRAINT fk_articles_category # 外部キー制約名
FOREIGN KEY (category_id) # 外部キーのカラム名
REFERENCES categories(id) # 外部キーの参照先:テーブル名(カラム名)
ON DELETE RESTRICT # 削除時の動作:子レコードがある時に親レコードの削除禁止
);
INSERT INTO articles (title, category_id) VALUES ('記事1-1', 1), ('記事1-2', 1), ('記事2', 2), ('記事3', 3);
次の SQL を実行すると、エラーが発生します。
SQL
DELETE FROM categories WHERE id = 1;
エラー
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test_db`.`articles`, CONSTRAINT `fk_articles_category` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`))
子レコードを削除してから親レコードを削除する場合は、削除できます。
SQL
# カテゴリー1 の記事を削除
DELETE FROM articles WHERE category_id = 1;
# カテゴリー1 を削除
DELETE FROM categories WHERE id = 1;
また、存在しない親の ID を指定して INSERT する場合もエラーになります。
SQL
INSERT INTO articles (title, category_id) VALUES ('記事999', 999);
エラー
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test_db`.`articles`, CONSTRAINT `fk_articles_category` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`))2.3 ON DELETE SET NULL
子レコードがある時に親レコードを削除した場合、子レコードの外部キーを NULL にして、子レコードだけ存在することを許容します。
例えば、カテゴリーと記事のように、カテゴリーに属さない記事があっても良い場合に使用します。
次の SQL を実行すると、削除した親レコードを参照していた子レコードの外部キーが NULL になります。
例えば、カテゴリーと記事のように、カテゴリーに属さない記事があっても良い場合に使用します。
SQL
CREATE TABLE articles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
category_id INT NULL,
CONSTRAINT fk_articles_category # 外部キー制約名
FOREIGN KEY (category_id) # 外部キーのカラム名
REFERENCES categories(id) # 外部キーの参照先:テーブル名(カラム名)
ON DELETE SET NULL # 削除時の動作:子レコードの外部キーを NULL にします
);
INSERT INTO articles (title, category_id) VALUES ('記事1-1', 1), ('記事1-2', 1), ('記事2', 2), ('記事3', 3);
次の SQL を実行すると、削除した親レコードを参照していた子レコードの外部キーが NULL になります。
SQL
DELETE FROM categories WHERE id = 1;
SELECT * FROM articles;
実行結果
+----+-----------+-------------+
| id | title | category_id |
+----+-----------+-------------+
| 1 | 記事1-1 | NULL |
| 2 | 記事1-2 | NULL |
| 3 | 記事2 | 2 |
| 4 | 記事3 | 3 |
+----+-----------+-------------+2.4 ON DELETE CASCADE
親レコードを削除すると、子レコードを自動的に削除します。
例えば、注文データと注文明細データのように、子レコードだけ存在しても意味がない場合に使用します。
ON DELETE RESTRICT では、削除を禁止することで整合性を保ちますが、
ON DELETE CASCADE は、子レコードを自動削除することで整合性を保ちます。
例えば、注文データと注文明細データのように、子レコードだけ存在しても意味がない場合に使用します。
ON DELETE RESTRICT では、削除を禁止することで整合性を保ちますが、
ON DELETE CASCADE は、子レコードを自動削除することで整合性を保ちます。
SQL
DELETE FROM categories;
INSERT INTO categories (id, name) VALUES (1, 'カテゴリー1'), (2, 'カテゴリー2'), (3, 'カテゴリー3');
CREATE TABLE articles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
category_id INT NULL,
CONSTRAINT fk_articles_category # 外部キー制約名
FOREIGN KEY (category_id) # 外部キーのカラム名
REFERENCES categories(id) # 外部キーの参照先:テーブル名(カラム名)
ON DELETE CASCADE # 削除時の動作:子レコードを削除します
);
INSERT INTO articles (title, category_id) VALUES ('記事1-1', 1), ('記事1-2', 1), ('記事2', 2), ('記事3', 3);
DELETE FROM categories WHERE id = 1;
SELECT * FROM articles;
categories を削除したのに、articles の記事1-1 と 記事1-2 も削除されている。実行結果
+----+---------+-------------+
| id | title | category_id |
+----+---------+-------------+
| 11 | 記事2 | 2 |
| 12 | 記事3 | 3 |
+----+---------+-------------+2.5 ON UPDATE CASCADE
親レコードのキーが変更されると、自動的に子レコードのキーも合わせて変更します。
但し、キーを変更することは少ないため、使用頻度は低い。
後から追加することもできるので、キーを変更する必要がないなら、最初は付けない方が良い。
但し、キーを変更することは少ないため、使用頻度は低い。
後から追加することもできるので、キーを変更する必要がないなら、最初は付けない方が良い。
SQL
DELETE FROM categories;
INSERT INTO categories (id, name) VALUES (1, 'カテゴリー1'), (2, 'カテゴリー2'), (3, 'カテゴリー3');
CREATE TABLE articles (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
category_id INT NULL,
CONSTRAINT fk_articles_category # 外部キー制約名
FOREIGN KEY (category_id) # 外部キーのカラム名
REFERENCES categories(id) # 外部キーの参照先:テーブル名(カラム名)
ON DELETE RESTRICT # 削除時の動作:子レコードがある時に親レコードの削除禁止
ON UPDATE CASCADE # 更新時の動作:親レコード
);
INSERT INTO articles (title, category_id) VALUES ('記事1-1', 1), ('記事1-2', 1), ('記事2', 2), ('記事3', 3);
UPDATE categories SET id = 999 WHERE id = 1;
SELECT * FROM articles;
実行結果
+----+-----------+-------------+
| id | title | category_id |
+----+-----------+-------------+
| 5 | 記事1-1 | 999 |
| 6 | 記事1-2 | 999 |
| 7 | 記事2 | 2 |
| 8 | 記事3 | 3 |
+----+-----------+-------------+2.6 既存のテーブルから外部キー制約の削除と追加
外部キー制約の削除
外部キー制約の追加
次のコマンドで、外部キー制約の有無を確認できます。
SQL
ALTER TABLE articles # テーブル名
DROP FOREIGN KEY fk_articles_category; # 外部キー制約名
外部キー制約の追加
SQL
ALTER TABLE articles
ADD CONSTRAINT fk_articles_category # 外部キー制約名
FOREIGN KEY (category_id) # 外部キーのカラム名
REFERENCES categories(id) # 外部キーの参照先:テーブル名(カラム名)
ON DELETE RESTRICT; # 削除時の動作:子レコードがある時に親レコードの削除禁止
次のコマンドで、外部キー制約の有無を確認できます。
SQL
SHOW CREATE TABLE articles;
