无法更改外键约束中使用的列


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
上面的示例来自《学习SQL,第二版》一书。希望作者Alan Beaulieu做出更正。
德米特里(Dmitry)

Answers:


126

外键字段和引用的类型和定义必须相同。这意味着您的外键不允许更改字段的类型。

一种解决方案是:

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要求对行进行更详细的说明“外键字段和引用的类型和定义必须相等。这意味着您的外键不允许更改字段的类型。”

MySQL 5.5参考手册:FOREIGN KEY约束

外键和引用键中的对应列在InnoDB中必须具有相似的内部数据类型,以便可以在不进行类型转换的情况下进行比较。整数类型的大小和符号必须相同。字符串类型的长度不必相同。对于非二进制(字符)字符串列,字符集和排序规则必须相同。


1
不要忘记为此使用事务。否则您的数据库可能会损坏。
弗朗索瓦·布尔乔亚

5
好一点,不幸的是,MySQL不支持围绕DDL语句的事务。在执行DDL查询之前,提交未完成的事务,请参见dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim,2012年

2
重新创建外键的正确语句应为:ALTER TALE favorite_food添加约束fk_fav_food_person_id FOREIGN KEY(person_id)参考person(id);
Felizardo

1
为什么person_id在删除外键后立即修改?看起来您没有更改任何内容,因为它已经是SMALLINT UNSIGNED
丹尼斯·苏巴切夫

1
我们不知道那是什么,因为他只发布了引用表结构。Innodb具有int作为内部类型,smallint等仅是快捷方式
Michel Feldheim,

198

您可以关闭外键检查:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

请确保不要在生产环境中使用此文件并进行备份。


1
似乎是非常不安全的解决方案。是否可能导致数据完整性损失?
2015年

@Synaps-是的,如果要删除/更新/插入则可以。如果您只是修改表或为数据库添加种子,则不会发生数据丢失,另一方面,您应该手动验证数据(因为删除了约束)
Dementic 2015年

2
此解决方案很棒,如果您在修改数据之前先写锁,然后在完成后解锁,则可以在生产环境中使用它。使用sql文件在尽可能短的时间内进行更改会更好。
弗朗西斯科·扎拉波佐

快速调整的好方法
Genaut

1
您不需要像SET FOREIGN_KEY_CHECKS会话作用域那样的锁(其他会话仍将应用FK约束)。它非常适合添加/删除AUTO_INCREMENT(它不会更改实际的列数据类型),但是 如果您尝试将列数据类型更改为“ real”(例如,从SMALLINT更改为INT),则它将不起作用,因为150 FK constraint incorrectly formed当mysql尝试用新表替换旧表。在这种情况下,请使用接受的答案。
Xenos

-3

设置键(主键或外键)时,您在设置如何使用它们的约束,从而限制了您对它们的操作。如果您确实想更改列,则可以重新创建没有约束的表,尽管我建议您反对它。一般而言,如果您有想要做某事的情况,但受到约束的限制,则最好通过更改您想做的事情而不是约束来解决。


12
这是此类无益的,无用的答案!
ajmedway

3
@ajmedway然后,您可以写一个有用的答案,而不必怪罪其他用户
我是最愚蠢的人

2
@IamtheMostStupidPerson这比不加评论地投票要好得多。至少评论者可以猜测为什么要投票。诸如“安全胜于遗憾”之类的通用答案没有用。
Csaba Toth
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.