概要
Djangoでマイグレーションを実施したら以下のエラーになりました。
MySQLdb._exceptions.IntegrityError: (1215, 'Cannot add foreign key constraint')
phpmyadminからSQLで実行してみても以下のエラーです。
MySQL のメッセージ: ドキュメント
#1215 - 外部キー制約を追加できません。
こちらのエラーを解決できたので、原因と対応法を紹介します。
考えられる主な原因
エラーメッセージは、MySQLが「外部キー制約を追加できない」と言っています。
一般的にありうるのは以下あたりだと思います。
- 参照されるテーブルまたはカラムが存在しない。
- 参照するカラムと参照されるカラムのデータ型が一致していない。
- 参照されるカラムが一意(ユニーク)でない = 一意性を持つインデックス(主キーまたは一意キー)になっていない。
- 参照されるテーブルの既存データが新たに設定しようとしている外部キー制約に違反する。
- (外部キー制約を追加するカラムがNULLを許容していない場合に)NULLを含むカラムを参照しようとしている
ググってみても、「データ型の不一致」「一意(ユニーク)でない」といった原因のものが大半。
しかしこれらは確認しましたが、問題ありませんでした。
何が原因なのだ...うぅむ。
答えは...照合順序の不一致だった
原因は、DBの「照合順序(collation)」にありました。
照合順序は、文字列の比較やソートの方法を決定する規則のこと。大文字小文字を区別する、しない、とかです。
MySQLでは、外部キーとして設定しようとしているカラムと、参照先のカラムの照合順序が一致していなければ外部キー制約の追加を拒否されるのです。
MYSQLのドキュメントにもちゃんと書いてあります。
外部キー内の対応するカラムと、参照されるキーは同様のデータ型を持っている必要があります。 「INTEGER や DECIMAL などの固定精度タイプのサイズと符号は同じである必要があります」。 文字列型の長さが同じである必要はありません。 バイナリ以外の (文字の) 文字列カラムの場合、文字セットと照合順序が同じである必要があります。
引用元:13.1.20.5 FOREIGN KEY の制約
よくよく確認すると、phpmyadminでDBを作る時、プルダウンメニューのデフォルトでutf8_unicode_ci
が選ばれています。
この状態で、DUMPされたSQLをインポートしたのですが、このDUMP元のDBの照合順序はutf8_general_ci
だったのです。mysqlのデフォルトはutf8_general_ci
なので、phpmyadminでのデフォルト(というかプルダウンで選択できるけど最初に選択されているものという意味)と一致していなかったのです...。
すると、マイグレーション時には、外部キーとして設定しようとしているカラムはutf8_unicode_ci
であるのに、参照先のカラム(DUMP元にあったテーブルのカラム)はutf8_general_ci
であるので、ここで不一致='Cannot add foreign key constraint'
エラーになったのです。
とほほ...。
phpmyadminでサクッとデータベース作ってDUMP取り込んでいましたが、次回からはちゃんと照合順序を確認しようという教訓になりました。
補足
-
utf8_general_ci
: 非常に基本的なUnicodeの比較ルールを使用。ひらがなカタカナの区別や英字の大文字小文字が区別ができるので、パフォーマンスは高いといえる。「レガシー照合」とか呼ばれているらしい。
= 正確性を重視! -
utf8_unicode_ci
: Unicodeの比較アルゴリズムを使用。これにより、特定の言語の特殊な規則をより正確に反映することはできるが、utf8_general_ci
よりもパフォーマンスは若干低い可能性があるとのこと(例:英字の大文字小文字が区別されない)。
= 速度を重視! - こちらの記事に詳細が書かされていますのでご参考に:【MySQL】照合順序とは?
参考記事
- 外部キーを追加できない問題を解決:Cannot add foreign key constraint
- あと、
charset
の不一致でも同様のエラーになるという記事もありました