106

我正在尝试使用两个外键在 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;

任何帮助将不胜感激。

4

20 回答 20

254

我有同样的问题ALTER TABLE ADD FOREIGN KEY

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

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

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

  3. Parent 表中引用的列必须是键的最左侧列。如果 Parent 中的键是PRIMARY KEYor ,则最好UNIQUE KEY

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

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

    例外:字符串的长度可能不同。例如,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. 如果您使用该选项声明 FK ON DELETE SET NULL,则 FK 列必须可以为空。

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

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

希望这可以帮助。

于 2011-01-12T20:39:06.183 回答
65

MySQL 的通用“errno 150”消息“<a href="http://dev.mysql.com/doc/refman/5.5/en/create-table-foreign-keys.html" rel="noreferrer">意味着外键约束没有正确形成。” 如果您正在阅读此页面,您可能已经知道,通用的“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表明 table 根本没有任何索引t1。通过在 上定义主键来修复该问题t1,并且将成功创建外键约束。

于 2013-08-12T05:47:01.897 回答
25

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

通常,ID 列上的“未签名”属性会让您感到困惑。

ALTER TABLE `dbname`.`tablename` CHANGE `fieldname` `fieldname` int(10) UNSIGNED NULL;
于 2009-10-27T15:58:25.327 回答
10

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

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

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

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

MySQL 5.1 参考手册

于 2009-09-21T23:14:16.307 回答
5

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

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

MySQL 外键错误和 Errno 150

于 2012-06-12T23:44:40.767 回答
4

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

于 2012-02-23T12:48:57.830 回答
3

绝对不是这样,但我发现这个错误很常见且不明显。a 的目标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));
于 2014-11-26T14:22:11.420 回答
3

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

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

于 2015-06-04T04:18:04.590 回答
3

请首先确保

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

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

于 2016-05-29T13:25:02.003 回答
2

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

    ---------------------------------------------------------------------------------------------------------+
| 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)                                                                                                                                                                           |
+---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+

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

于 2013-11-27T10:40:22.937 回答
1

I've found another reason this fails... case sensitive table names.

For this table definition

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

This table definition works

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

whereas this one fails

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

The fact that it worked on Windows and failed on Unix took me a couple of hours to figure out. Hope that helps someone else.

于 2014-08-02T05:54:17.980 回答
1

当您尝试将文件源代码到现有数据库时,通常会发生这种情况。首先删除所有表(或数据库本身)。然后源文件SET foreign_key_checks = 0;在开头和SET foreign_key_checks = 1;结尾。

于 2013-03-27T20:14:13.957 回答
1

适用于 Mac OS 的 MySQL Workbench 6.3。

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

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

在此处输入图像描述

于 2015-08-24T04:44:13.607 回答
0

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

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

于 2014-10-07T12:48:35.250 回答
0

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

于 2013-01-22T14:27:21.123 回答
0

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

于 2013-06-04T15:21:56.670 回答
0

当外键约束是基于varchar类型时,那么除了目标列提供的列表marv-el之外,还必须有唯一约束。

于 2015-05-14T14:42:43.583 回答
0

(旁注太大,无法评论)

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

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

由于您可能想要映射两个方向,因此还要INDEX以相反的顺序添加这两列。(没有必要这样做UNIQUE。)

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

于 2018-02-14T03:00:07.110 回答
0

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

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

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

SET FOREIGN_KEY_CHECKS = 1;

 

-- 不检查外键约束

设置 FOREIGN_KEY_CHECKS = 0;

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

于 2019-09-06T11:50:53.020 回答
-1

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

于 2018-06-30T06:02:02.097 回答