何が起きているか
外部キーを追加しようとすると、MySQLが以下のエラーを返します:
ERROR 1215 (HY000): Cannot add foreign key constraint
非常に曖昧なメッセージです。MySQLはなぜ失敗したのかを教えてくれません。少なくとも5つの異なる根本原因があり、それらはすべて全く同じメッセージを出力します。このガイドでは各原因を取り上げ、エラー出力を具体的な解決策に対応させられるようにします。
ステップ1:実際のエラー原因を確認する
推測は禁物です。失敗したステートメントの直後に以下を実行してください:
SHOW ENGINE INNODB STATUS\G
LATEST FOREIGN KEY ERRORセクションまでスクロールすると、次のような内容が表示されます:
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
これで調査の手がかりが得られました。以下の原因のいずれかと照合してください。
よくある原因と修正方法
原因1:データ型の不一致
最も多い原因です。子カラムと親カラムは、サイズや符号なし(UNSIGNED)の有無も含めて、完全に同じデータ型でなければなりません。一つでも不一致があると制約が失敗します。
-- 親テーブル
CREATE TABLE users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY
);
-- これは失敗する — INT と INT UNSIGNED の不一致
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT, -- UNSIGNED が抜けている
FOREIGN KEY (user_id) REFERENCES users(id)
);
修正:型を完全に一致させてください。
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED, -- users.id と一致
FOREIGN KEY (user_id) REFERENCES users(id)
);
まず親カラムを必ず確認してください:
SHOW CREATE TABLE users;
原因2:参照先カラムにインデックスがない
MySQLは参照先カラムがPRIMARY KEYであるか、UNIQUEインデックスを持っていることを要求します。インデックスのない通常のカラムを参照すると、毎回このエラーが発生します。
-- users.email にインデックスがない → FK が失敗
CREATE TABLE sessions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_email VARCHAR(255),
FOREIGN KEY (user_email) REFERENCES users(email) -- エラー!
);
まず親カラムにインデックスを追加してから、再試行してください:
ALTER TABLE users ADD UNIQUE INDEX idx_email (email);
原因3:ストレージエンジンがInnoDBではない
外部キーはInnoDB専用の機能です。MyISAMは外部キーをサポートしておらず、MySQLのバージョンによっては制約が完全に失敗するか、無視されることがあります。
SHOW CREATE TABLE users;
出力にENGINE=MyISAMが表示されている場合は、両方のテーブルを変換してください:
ALTER TABLE users ENGINE=InnoDB;
ALTER TABLE orders ENGINE=InnoDB;
新しいテーブルのデフォルトを自動的にInnoDBにしたい場合は:
SET default_storage_engine=InnoDB;
原因4:文字セットまたは照合順序の不一致
両方のカラムがVARCHARであれば問題ないように見えます。しかし、一方がutf8を使用し、もう一方がutf8mb4を使用している場合、または照合順序が異なる場合、MySQLはそれらを互換性のない型として扱います。
-- 現在の照合順序を確認
SHOW CREATE TABLE users;
SHOW CREATE TABLE orders;
一方にutf8_general_ci、もう一方にutf8mb4_unicode_ciのような不一致が見られる場合は、揃えてください:
ALTER TABLE orders MODIFY user_email VARCHAR(255)
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
原因5:既存データが制約に違反している
既にデータが入っているテーブルにFKを追加する場合、MySQLはすべての行を検証します。ordersテーブルのuser_idでusersテーブルに対応する行がないものがあると、操作全体がブロックされます。
-- 孤立した行を検索
SELECT o.id, o.user_id
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE u.id IS NULL;
まず孤立行を削除するか、NULLに設定するか(カラムがNULLを許容する場合)、または不足している親レコードを挿入してから再試行してください。
原因6:参照先テーブルがまだ存在しない
マイグレーションやダンプファイルではテーブルの順序が重要です。usersが存在する前にordersを作成すると、参照先がないためFKはすぐに失敗します。
親テーブルが先になるようCREATE TABLE文の順序を変更してください。ダンプをインポートする際は、順序の問題を回避するためにFOREIGN_KEY_CHECKSを無効にして全体をラップしてください:
SET FOREIGN_KEY_CHECKS=0;
-- すべての CREATE TABLE 文
SET FOREIGN_KEY_CHECKS=1;
最後に再度有効にして、制約が今後も適切に適用されるようにしてください。
確認:修正が機能したことを確認する
FKが追加されたら、information_schemaでアクティブであることを確認してください:
SELECT
CONSTRAINT_NAME,
TABLE_NAME,
COLUMN_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM information_schema.KEY_COLUMN_USAGE
WHERE TABLE_SCHEMA = 'your_database_name'
AND REFERENCED_TABLE_NAME IS NOT NULL;
新しい制約が結果に表示されるはずです。次に簡単な動作確認を行ってください:
-- これはFK違反で失敗するはず — 制約がアクティブであることを確認
INSERT INTO orders (user_id) VALUES (99999); -- user 99999 は存在しない
-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails
ここでERROR 1452が表示されれば期待通りの結果です。外部キーが参照整合性を強制していることが証明されます。
クイックリファレンスチェックリスト
SHOW ENGINE INNODB STATUS\Gを実行して実際のエラーメッセージを確認する- 両方のカラムは(
UNSIGNEDも含めて)同一の型でなければならない - 参照先カラムはPKであるか、UNIQUE/INDEXを持っていなければならない
- 両方のテーブルは
ENGINE=InnoDBを使用しなければならない - VARCHARカラムの文字セットと照合順序は一致していなければならない
- FK追加前に子テーブルに孤立行がないこと
- 子テーブルが参照する前に親テーブルが存在していなければならない
これらのエラーの背景にあるパターン
ERROR 1215はあらゆる原因をまとめたエラーです。MySQLは最初に見つけた違反で停止し、実際の原因に関わらず同じ汎用メッセージを報告します。ぜひ身につけてほしい習慣は、失敗のたびにSHOW ENGINE INNODB STATUS\Gをすぐに実行することです。このコマンド一つでデバッグ時間を30分から約2分に短縮できます。
新しいスキーマでは、親テーブルを先に定義してください。関連するカラム間では一貫した型を使用し、自動インクリメントのPKにはINT UNSIGNEDを使用してください。データベース全体の文字セットを最初から一つに決めてください。2024年以降はutf8mb4が正しいデフォルトです。外部キーエラーのほとんどは謎ではありません。時間をかけて蓄積された小さな不整合が、ついに制約によって検出された結果です。

