外部キー制約で使用されている列は変更できません


111

テーブルを変更しようとしたときにこのエラーが発生しました。

Error Code: 1833. Cannot change column 'person_id': used in a foreign key constraint 'fk_fav_food_person_id' of table 'table.favorite_food'

以下は、正常に実行された私のCREATE TABLE STATEMENTです。

CREATE TABLE favorite_food(
    person_id SMALLINT UNSIGNED,
    food VARCHAR(20),
    CONSTRAINT pk_favorite_food PRIMARY KEY(person_id,food),
    CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

次に、このステートメントを実行しようとすると、上記のエラーが発生しました。

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

4
上記の例は、本「Learning SQL、2nd edition」からのものです。作者のアラン・ボーリューが訂正してくれることを願っています。
ドミトリー

回答:


126

外部キーフィールドと参照のタイプと定義は同じでなければなりません。これは、外部キーがフィールドのタイプの変更を許可しないことを意味します。

1つの解決策は次のとおりです。

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

ALTER TABLE favorite_food
    DROP FOREIGN KEY fk_fav_food_person_id,
    MODIFY person_id SMALLINT UNSIGNED;

これでperson_idを変更できます

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

外部キーを再作成する

ALTER TABLE favorite_food
    ADD CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
          REFERENCES person (person_id);

UNLOCK TABLES;

編集: コメントのおかげで、上記のロックを追加しました

これを実行している間は、データベースへの書き込みを禁止する必要があります。そうしないと、データの整合性の問題が発生するおそれがあります。

上に書き込みロックを追加しました

独自の(INSERT, UPDATE, DELETE)以外のセッションでクエリを書き込むと、タイムアウトになるまで待機しUNLOCK TABLESます。実行されます

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

編集2:OPは、行の詳細な説明を求めました。「外部キーフィールドと参照のタイプと定義は同じである必要があります。これは、外部キーがフィールドのタイプの変更を許可しないことを意味します。」

FOREIGN KEY制約:5.5リファレンスマニュアルのMySQL

タイプ変換なしで比較できるように、外部キーと参照キーの対応する列はInnoDB内で同様の内部データ型を持っている必要があります。整数型のサイズと符号は同じでなければなりません。文字列型の長さは同じである必要はありません。非バイナリ(文字)文字列の列の場合、文字セットと照合順序は同じでなければなりません。


1
これにはトランザクションを使用することを忘れないでください。そうしないと、データベースが破損する可能性があります。
フランソワブルジョワ

5
良い点、残念ながらMySQLはDDLステートメントに関するトランザクションをサポートしていません。開いているトランザクションは、DDLクエリが実行される前にコミットされます。dev.mysql.com/ doc / refman / 5.5
Michel

2
外部キーを再作成する正しいステートメントは次のようになります。ALTER TALE favorite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY(person_id)REFERENCES person(id);
Felizardo 2016年

1
person_id外部キーを削除した直後に変更するのはなぜですか?既になので、何も変更しなかったようですSMALLINT UNSIGNED
Dennis Subachev

1
彼が参照しているテーブル構造だけを投稿したので、それが何であったかはわかりません。Innodbは内部型としてintを持ち、smallintなどはショートカットのみです
Michel Feldheim '20年

198

外部キーのチェックをオフにすることができます:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

これを本番環境で使用しないでください。バックアップを作成してください。


1
非常に不安定なソリューションのようです。データの整合性が失われる可能性がありますか?
15

@Synaps-はい、削除/更新/挿入する場合は可能です。テーブルを変更するか、データベースにシードするだけの場合、データ損失は発生しません。一方で、手動でデータを検証する必要があります(制約を削除するため)
認知症

2
このソリューションは優れており、データを変更する前にロックを書き込み、完了したらロックを解除すれば、本番環境で使用できます。SQLファイルを使用して、可能な限り最短時間で変更を行うと、さらに良いでしょう。
フランシスコサラボゾ2018

迅速な調整のための優れたソリューション
Genaut

1
SET FOREIGN_KEY_CHECKSセッションスコープのようにロックは必要ありません(他のセッションには引き続きFK制約が適用されます)。これの追加/削除のための完璧なAUTO_INCREMENT(実際の列のデータ型を変更しない)が、それは動作しません あなたが合法的に取得しますと、あなたが「本物」の列のデータ型を変更しようとした場合(SMALLINTからINTに、言います)150 FK constraint incorrectly formedmysqlは古いテーブルを新しいテーブルに置き換えようとします。そのような場合は、受け入れられた回答を使用してください。
Xenos

-3

キー(主キーまたは外部キー)を設定すると、キーの使用方法に制約が設定されます。これにより、キーで実行できることが制限されます。本当に列を変更したい場合は、制限なしでテーブルを再作成できますが、これはお勧めしません。一般的に言えば、何かを実行したいが制約によってブロックされている場合は、制約ではなく実行したいことを変更することで解決できます。


12
これは、そのような役に立たない、役に立たない答え!
ajmedway 2017

3
@ajmedwayその後、他のユーザーを非難することなく、有用な答えを書くことができます
私はほとんど愚かな人です

2
@IamtheMostStupidPersonコメントなしで単に投票するよりもはるかに優れています。少なくともコメント投稿者は、反対票を投じる理由を推測できます。「申し訳ありませんが安全である方がいい」のような一般的な答えは役に立ちません。
Csaba Toth 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.