【问题标题】:Cannot change column used in a foreign key constraint无法更改外键约束中使用的列
【发布时间】:2012-11-28 13:42:56
【问题描述】:

当我试图改变我的桌子时,我得到了这个错误。

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;

【问题讨论】:

  • 上面的例子来自《Learning SQL, 2nd edition》一书。我希望作者 Alan Beaulieu 做出更正。

标签: mysql


【解决方案1】:

您可以关闭外键检查:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

请确保不要在生产中使用它并进行备份。

【讨论】:

  • 似乎是非常不安全的解决方案。是否可能导致数据完整性丢失?
  • @Synaps - 是的,如果您正在删除/更新/插入,它可以。如果您只是修改表或播种数据库,则不会发生数据丢失,另一方面,您应该手动验证您的数据(因为您删除了约束)
  • 这个解决方案很棒,如果您在修改数据之前添加写锁,然后在完成后解锁,您可以在生产环境中使用它。使用 sql 文件在尽可能短的时间内进行更改会更好。
  • 快速调整的好解决方案
  • 您不需要锁定,因为SET FOREIGN_KEY_CHECKS 是会话范围的(其他会话仍将应用 FK 约束)。它非常适合添加/删除 AUTO_INCREMENT(它不会更改实际的列数据类型),但如果您尝试将列数据类型更改为“真实”(例如,从 SMALLINT 到INT),因为当 mysql 尝试用新表替换旧表时,您将获得合法的150 FK constraint incorrectly formed。在这种情况下,请使用接受的答案。
【解决方案2】:

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

一个解决方案是这样的:

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;

编辑: 上面加了锁,感谢 cmets

您必须在执行此操作时禁止写入数据库, 否则您将面临数据完整性问题的风险。

我在上面加了写锁

除您自己的会话(INSERT, UPDATE, DELETE)之外的任何其他会话中的所有写入查询都将等到超时或UNLOCK TABLES;被执行

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

编辑 2:OP 要求更详细地解释“外键字段和引用的类型和定义必须相等。这意味着您的外键不允许更改字段的类型。”

来自MySQL 5.5 Reference Manual: FOREIGN KEY Constraints

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

【讨论】:

  • 不要忘记为此使用事务。否则您的数据库可能会损坏。
  • 好点,不幸的是 MySQL 不支持围绕 DDL 语句的事务。在执行 DDL 查询之前提交打开的事务,请参阅 dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
  • 重新创建外键的正确语句是:ALTER TALE favorite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCES person(id);
  • 为什么在删除外键后马上修改person_id?看起来您没有更改任何内容,因为它已经是 SMALLINT UNSIGNED
  • 我们不知道它是什么,因为他只发布了引用表结构。 Innodb 有 int 作为内部类型,smallint 等只是快捷方式
【解决方案3】:

就我而言,有必要添加GLOBAL

SET FOREIGN_KEY_CHECKS = 0;
SET GLOBAL FOREIGN_KEY_CHECKS=0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;
SET GLOBAL FOREIGN_KEY_CHECKS=1;

【讨论】:

    【解决方案4】:

    转到相关表的结构选项卡。 在操作下,您有索引。 放下它们

    完成必要的修改后, 带回外键并恢复已删除的索引。然后确保你的结构是相同的并且没有改变

    【讨论】:

      【解决方案5】:

      当您设置键(主键或外键)时,您是在设置如何使用它们的限制,这反过来又限制了您可以用它们做什么。如果你真的想改变列,你可以重新创建没有约束的表,尽管我不建议这样做。一般来说,如果你有一种情况,你想做某事,但它被一个约束所阻止,最好通过改变你想做的事情而不是约束来解决。

      【讨论】:

      • @ajmedway 这样你就可以写一个有用的答案而不责怪其他用户了
      • @IamtheMostStupidPerson 这比不发表评论就投反对票要好得多。至少评论者可以猜到为什么投反对票。诸如“安全总比抱歉”之类的通用答案没有用。
      猜你喜欢
      • 1970-01-01
      • 2014-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-26
      • 2013-05-04
      相关资源
      最近更新 更多