【问题标题】:MySQL Creating tables with Foreign Keys giving errno: 150MySQL使用外键创建表,给出错误号:150
【发布时间】:2010-11-30 05:57:43
【问题描述】:

我试图在 MySQL 中创建一个带有两个外键的表,这两个外键引用了另外 2 个表中的主键,但是我收到了 errno: 150 错误,它不会创建表。

这是所有 3 个表的 SQL:

CREATE TABLE role_groups (
  `role_group_id` int(11) NOT NULL `AUTO_INCREMENT`,
  `name` varchar(20),
  `description` varchar(200),
  PRIMARY KEY (`role_group_id`)
) ENGINE=InnoDB;

CREATE TABLE IF NOT EXISTS `roles` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50),
  `description` varchar(200),
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB;

create table role_map (
  `role_map_id` int not null `auto_increment`,
  `role_id` int not null,
  `role_group_id` int not null,
  primary key(`role_map_id`),
  foreign key(`role_id`) references roles(`role_id`),
  foreign key(`role_group_id`) references role_groups(`role_group_id`)
) engine=InnoDB;

任何帮助将不胜感激。

【问题讨论】:

  • 您能否发布错误输出并告诉我们哪个命令(三个)导致错误?
  • auto_increment 周围的反引号是怎么回事?那是无效的。 Auto_increment 是关键字,而不是标识符。

标签: mysql foreign-keys mysql-error-150


【解决方案1】:

ALTER TABLE ADD FOREIGN KEY 也有同样的问题。

一个小时后,我发现必须满足这些条件才不会出现错误 150:

  1. 父表必须存在,然后才能定义外键来引用它。您必须以正确的顺序定义表:首先是父表,然后是子表。如果两个表相互引用,则必须创建一个没有 FK 约束的表,然后创建第二个表,然后使用 ALTER TABLE 将 FK 约束添加到第一个表。

  2. 这两个表必须都支持外键约束,即ENGINE=InnoDB。其他存储引擎会默默地忽略外键定义,因此它们不会返回错误或警告,但不会保存 FK 约束。

  3. 父表中引用的列必须是键的最左侧列。最好在 Parent 中的键是 PRIMARY KEYUNIQUE KEY

  4. FK 定义必须以与 PK 定义相同的顺序引用 PK 列。例如,如果 FK REFERENCES Parent(a,b,c) 则 Parent 的 PK 不能按顺序在列上定义 (a,c,b)

  5. Parent 表中的 PK 列必须与 Child 表中的 FK 列具有相同的数据类型。例如,如果 Parent 表中的 PK 列是 UNSIGNED,请务必在 Child 表字段中为相应列定义 UNSIGNED

    例外:字符串的长度可能不同。例如,VARCHAR(10) 可以引用 VARCHAR(20),反之亦然。

  6. 任何字符串类型的 FK 列必须与对应的 PK 列具有相同的字符集和排序规则。

  7. 如果 Child 表中已有数据,则 FK 列中的每个值都必须与 Parent 表 PK 列中的值匹配。用这样的查询来检查这个:

    SELECT COUNT(*) FROM Child LEFT OUTER JOIN Parent ON Child.FK = Parent.PK 
    WHERE Parent.PK IS NULL;
    

    这必须返回零 (0) 个不匹配的值。显然,这个查询是一个通用的例子;您必须替换您的表名和列名。

  8. Parent 表和 Child 表都不能是 TEMPORARY 表。

  9. Parent 表和 Child 表都不能是 PARTITIONED 表。

  10. 如果您使用 ON DELETE SET NULL 选项声明 FK,则 FK 列必须可以为空。

  11. 如果为外键声明约束名称,则约束名称在整个架构中必须是唯一的,而不仅仅是在定义约束的表中。两个表可能没有自己的同名约束。

  12. 如果其他表中有任何其他 FK 指向您尝试为其创建新 FK 的同一字段,并且它们格式错误(即不同的排序规则),则需要首先使它们保持一致。这可能是过去更改的结果,其中SET FOREIGN_KEY_CHECKS = 0; 与错误定义的不一致关系一起使用。有关如何识别这些问题 FK 的说明,请参阅下面的 @andrewdotn 回答。

希望这会有所帮助。

【讨论】:

  • 还有一点值得补充的:如果父表的PK多于一个字段,则FK中字段的顺序必须与PK中的顺序一致
  • 这包括 int(11) unsigned NOT NULLint(11) NOT NULL 之类的东西。
  • ALTER TABLE table_name ENGINE=InnoDB;
  • 如果表定义为 ENGINE=MyISAM,它不会生成 errno 150,因为它忽略外键声明。这就像说避免汽车发动机故障的最佳方法是驾驶船。 :-)
  • 另外,如果您的 CONSTRAINT 的 ON DELETE 规则是 SET NULL,请确保外键实际上可以为 NULL!我花了 30 分钟一遍又一遍地阅读这个答案,确保我的表符合条件但仍然收到错误 150。然后我注意到我的 FK 是一个 NOT NULL 字段,这意味着该规则无法应用。
【解决方案2】:

MySQL 的通用“errno 150”消息“means that a foreign key constraint was not correctly formed”。如果您正在阅读此页面,您可能已经知道,通用的“errno:150”错误消息确实没有帮助。然而:

您可以通过运行SHOW ENGINE INNODB STATUS; 然后在输出中查找LATEST FOREIGN KEY ERROR 来获得实际错误消息。

例如,这个尝试创建外键约束:

CREATE TABLE t1
(id INTEGER);

CREATE TABLE t2
(t1_id INTEGER,
 CONSTRAINT FOREIGN KEY (t1_id) REFERENCES t1 (id));

失败并出现错误Can't create table 'test.t2' (errno: 150)。除了这是一个外键问题之外,这并没有告诉任何人任何有用的信息。但是运行SHOW ENGINE INNODB STATUS;,它会说:

------------------------
LATEST FOREIGN KEY ERROR
------------------------
130811 23:36:38 Error in foreign key constraint of table test/t2:
FOREIGN KEY (t1_id) REFERENCES t1 (id)):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.

它说问题是找不到索引。 SHOW INDEX FROM t1 表明表 t1 根本没有任何索引。修复这个问题,比如在t1 上定义一个主键,就会成功创建外键约束。

【讨论】:

  • SHOW ENGINE INNODB STATUS 帮助我立即发现了一个我试图诊断近一个小时的问题。谢谢。
  • 在我的情况下,这表明一个完全不同的表,FK'd 我试图指向的同一个字段是不一致的,因此不会保存新的......假设这是使用SET FOREIGN_KEY_CHECKS = 0; 在一次或多次格式错误的导入/更改期间。很大的帮助,谢谢。
【解决方案3】:

确保您尝试通过约束链接的两个字段的属性完全相同。

通常,ID 列上的 'unsigned' 属性会引起您的注意。

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;

【讨论】:

  • 根据我的经验,值得在主表上使用 MySQL 的 SHOW CREATE TABLE 来准确检查针对主索引列设置的标志,然后将它们复制到外键列。那里可能有一些不明显的东西,比如“未签名”。
【解决方案4】:

当您运行此脚本时,您的数据库的当前状态是什么?它完全是空的吗?从头开始创建数据库时,您的 SQL 对我来说运行良好,但 errno 150 通常与删除和重新创建作为外键一部分的表有关。我感觉您没有使用 100% 全新的数据库。

如果您在“source”-ing SQL 文件时出错,您应该能够在“source”命令之后立即从 MySQL 提示符运行命令“SHOW ENGINE INNODB STATUS”以查看更详细的错误信息.

您可能还想查看手动输入:

如果您重新创建已删除的表,则它必须具有符合引用它的外键约束的定义。如前所述,它必须具有正确的列名和类型,并且必须在引用的键上具有索引。如果不满足这些,MySQL 将返回错误号 1005 并在错误消息中引用错误 150。如果 MySQL 从 CREATE TABLE 语句中报告错误号 1005,并且错误消息引用错误 150,则表创建失败,因为没有正确形成外键约束。

——MySQL 5.1 reference manual.

【讨论】:

    【解决方案5】:

    对于正在查看此线程并遇到相同问题的人:

    出现这样的错误有很多原因。有关 MySQL 中外键错误的原因和解决方案的完整列表(包括此处讨论的那些),请查看此链接:

    MySQL Foreign Key Errors and Errno 150

    【讨论】:

      【解决方案6】:

      对于通过 Google 找到此 SO 条目的其他人:确保您没有尝试对定义为“NOT NULL”的外键(待)列执行 SET NULL 操作。这让我非常沮丧,直到我记得做一个 CHECK ENGINE INNODB STATUS。

      【讨论】:

        【解决方案7】:

        当然不是这样,但我发现这个错误很常见且不明显。 FOREIGN KEY 的目标可能不是 PRIMARY KEY。对我有用的答案是:

        FOREIGN KEY 必须始终指向其他表的 PRIMARY KEY true 字段。

        CREATE TABLE users(
           id INT AUTO_INCREMENT PRIMARY KEY,
           username VARCHAR(40));
        
        CREATE TABLE userroles(
           id INT AUTO_INCREMENT PRIMARY KEY,
           user_id INT NOT NULL,
           FOREIGN KEY(user_id) REFERENCES users(id));
        

        【讨论】:

          【解决方案8】:

          正如@andrewdotn 所指出的,最好的方法是查看详细的错误(SHOW ENGINE INNODB STATUS;),而不仅仅是错误代码。

          其中一个原因可能是同名索引已经存在,可能在另一个表中。作为一种实践,我建议在索引名称之前添加表名前缀以避免此类冲突。例如而不是idx_userId 使用idx_userActionMapping_userId

          【讨论】:

            【解决方案9】:

            请先确认

            1. 您正在使用 InnoDB 表。
            2. FOREIGN KEY 字段的类型和长度 (!) 与源字段相同。

            我遇到了同样的问题,我已经解决了。我有一个字段的无符号 INT 和其他字段的整数。

            【讨论】:

              【解决方案10】:

              有用的提示,在尝试您的 CREATE 查询后使用 SHOW WARNINGS;,您将收到错误以及更详细的警告:

                  ---------------------------------------------------------------------------------------------------------+
              | Level   | Code | Message                                                                                                                                                                                                                                 |
              +---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+
              | Warning |  150 | Create table 'fakeDatabase/exampleTable' with foreign key constraint failed. There is no index in the referenced table where the referenced columns appear as the first columns.
              |
              | Error   | 1005 | Can't create table 'exampleTable' (errno:150)                                                                                                                                                                           |
              +---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+
              

              所以在这种情况下,是时候重新创建我的表了!

              【讨论】:

                【解决方案11】:

                这通常在您尝试将文件源文件导入现有数据库时发生。 首先删除所有表(或数据库本身)。 然后源文件以SET foreign_key_checks = 0;开头,SET foreign_key_checks = 1;结尾。

                【讨论】:

                  【解决方案12】:

                  我找到了另一个失败的原因...区分大小写的表名。

                  对于这个表定义

                  CREATE TABLE user (
                    userId int PRIMARY KEY AUTO_INCREMENT,
                    username varchar(30) NOT NULL
                  ) ENGINE=InnoDB;
                  

                  这个表定义有效

                  CREATE TABLE product (
                    id int PRIMARY KEY AUTO_INCREMENT,
                    userId int,
                    FOREIGN KEY fkProductUser1(userId) REFERENCES **u**ser(userId)
                  ) ENGINE=InnoDB;
                  

                  而这个失败了

                  CREATE TABLE product (
                    id int PRIMARY KEY AUTO_INCREMENT,
                    userId int,
                    FOREIGN KEY fkProductUser1(userId) REFERENCES User(userId)
                  ) ENGINE=InnoDB;
                  

                  它在 Windows 上运行而在 Unix 上失败的事实让我花了几个小时才弄清楚。希望对其他人有所帮助。

                  【讨论】:

                    【解决方案13】:

                    适用于 Mac OS 的 MySQL Workbench 6.3。

                    问题:尝试在 DB 图上执行正向工程时,表 X 上出现 errno 150,21 个中的 20 个成功,1 个失败。如果表 X 上的 FK 被删除,错误会转移到之前没有失败的另一个表。

                    将所有表引擎更改为 myISAM,它工作得很好。

                    【讨论】:

                      【解决方案14】:

                      还值得检查您是否无意中操作了错误的数据库。如果外表不存在,就会出现这个错误。为什么 MySQL 必须如此神秘?

                      【讨论】:

                        【解决方案15】:

                        确保外键在父级中未列为唯一。我遇到了同样的问题,我通过将其划分为不唯一来解决它。

                        【讨论】:

                          【解决方案16】:

                          在我的情况下,这是因为作为外键字段的字段名称太长,即。 foreign key (some_other_table_with_long_name_id)。尝试更短。在这种情况下,错误消息有点误导。

                          此外,正如 @Jon 前面提到的 - 字段定义必须相同(注意 unsigned 子类型)。

                          【讨论】:

                            【解决方案17】:

                            (旁注太大,无法评论)

                            映射表中不需要AUTO_INCREMENT id;摆脱它。

                            PRIMARY KEY 更改为(role_id, role_group_id)(按任意顺序)。这将使访问速度更快。

                            由于您可能想要映射两个方向,因此还要添加一个INDEX,这两列以相反的顺序排列。 (不用发UNIQUE。)

                            更多提示:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta

                            【讨论】:

                              【解决方案18】:

                              当外键约束基于varchar类型时,那么除了list provided by marv-el之外,目标列必须有唯一性约束。

                              【讨论】:

                                【解决方案19】:

                                在创建表之前执行以下行: SET FOREIGN_KEY_CHECKS = 0;

                                FOREIGN_KEY_CHECKS 选项指定是否检查 InnoDB 表的外键约束。

                                -- 指定检查外键约束(默认)

                                SET FOREIGN_KEY_CHECKS = 1;
                                

                                 

                                -- 不检查外键约束

                                SET FOREIGN_KEY_CHECKS = 0;

                                何时使用: 当您需要重新创建表并以任何父子顺序加载数据时,临时禁用引用约束(将 FOREIGN_KEY_CHECKS 设置为 0)很有用

                                【讨论】:

                                  【解决方案20】:

                                  我遇到了同样的问题,但我检查发现我没有父表。所以我只是在子迁移之前编辑父迁移。去做吧。

                                  【讨论】:

                                  • 这应该是评论而不是答案
                                  猜你喜欢
                                  • 2022-11-14
                                  • 2013-12-08
                                  • 1970-01-01
                                  相关资源
                                  最近更新 更多