外部キーを追加できない問題を解決:Cannot add foreign key constraint

外部キー制約を設定しようと思ったら Cannot add foreign key constraint が発生して追加できませんでした。

エラーの原因と解決方法

自分の状況では新規テーブルを作成、sequelizeを使ってマイグレーションしようとしたところで失敗。エラーが上記のものって感じです。

結論から言うと、今回は型と照合順序(collation)が違っていることが原因でした。外部キー制約を設定する場合には、対象となるカラムの型や文字コードが同じである必要があります。

なので、定義が合っていないカラムを調べて、同じ定義になるようにすればエラーを解決できます。カラムの設定を確認する方法は次に紹介します。

外部キーが追加できない Cannot add foreign key constraint
データベースとしてMySQLを使用しています。 外部キーを追加したいのですが、`Cannot add foreign key constraint`と表示されて追加することができません。 他にメッ

カラムの設定を確認する方法

では、外部キー制約を貼るカラムと対象のカラムの方が同じかどうか確認してみます。それぞれのカラムの設定を確認するには次のようなコマンドが使えます。

SHOW CREATE TABLE table_name;

このコマンドではテーブルを作成するときのCREATE文を見ることができます。

SHOW FULL COLUMNS FROM table_name;

このコマンドでは、カラムごとの型、照合順序、NULL制約、キー制約、デフォルト、Extra、権限、コメントを確認することができます。

DESC table_name;

型やキー制約だけ確認できれば良い場合はこのコマンドでも確認できます。

さらに詳しく

照合順序(collation)とは

今回は型と照合順序が違っていました。型については予測もできてすぐに修正できましたが、照合順序のことがすっかり抜けていました。

照合順序とはなんでしょうか。

照合順序は、データの文字列を比較する際の基準、ルールです。例えば、「a」と「A」は小文字と大文字の違いはありますがどちらも同じアルファベットです。これを区別するかしないかの基準が照合順序です。日本語で言うと「あ」と「ア」は区別するかなどです。

照合順序はすでに定義されたものが存在されていて、それを指定する形になっています。具体的な種類の説明については省略します。

なぜ今回のようなことが起こったのかを考えてみました。

自分の環境のデータベースは、より厳密にデータを区別するために、以前照合順序を変更したことがありました。データベース全体の照合順序のデフォルトを変更したのです。これによって新たに作られるテーブルでは、特別に照合順序を指定しない限りは変更後の照合順序が適用されます。

問題はここにありました。実は新しく作ったテーブルの照合順序には新しい照合順序が適用されるのですが、それまでに作られたテーブルの照合順序は変更されません。このことが原因で、以前からあったテーブルのカラムを外部キーに設定しようとしたときに、新しいテーブルと照合順序の違いが発生していたのです。

sequelizeでのcreate table、collationの設定

https://sequelize.org/master/class/lib/dialects/abstract/query-interface.js~QueryInterface.html#instance-method-createTable

sequelizeでCREATE TABLEする際の設定方法へのリンクです。公式ドキュメントにはしっかり照合順序の設定方法が記載されています。

Node, Sequelize, Mysql - How to define collation and charset to models?
Im using sequelize /w node and node-mysql. I create models using the sequelize-cli, and this is the result: 'use strict'; module.exports = function(seque...

一応、設定方法を調べていたときに見つけた stackoverflow のリンクも掲載しておきます。

sequelizeのマイグレーション履歴を変更する

sequelize cliを使ったマイグレーションに失敗した後、再度マイグレーションを実行しようと思いました。

テーブルをdropしてもう一度実行すれば良いだろうと考えていましたが、マイグレーションファイルが同じなので、マイグレーションが不要であると判断されてしまい実行されませんでした。

(ファイル名を変更すればマイグレーションは実行されます。あとundoできるのでそちらのほうがよかったかも)

この問題を解消するために、Sequelize Meta テーブルを変更することにしました。マイグレーションの履歴は、対象となるデータベースの Sequelize Meta テーブルに記録されています。このテーブルを実際に見てみると分かりますが、実行された順にマイグレーションファイルのファイル名が記録されています。

そして、該当のファイル名のレコードを削除すると、同名のマイグレーションファイルは新たなテーブル定義として認識され、マイグレーションの対象にすることができます。

Node.jsのSequelizeでDBのmigrationを実行する - Qiita
Node.jsのORMである Sequelize はその基となったActiveRecordと同様にDBのマイグレーション機構も備わっています。 Sequelize のマイグレーション操作についてはま…
タイトルとURLをコピーしました