MySQL使用外键创建表,给出错误:150

时间:2009-09-21 22:55:47

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

我正在尝试使用两个外键在MySQL中创建一个表,它引用了另外两个表中的主键,但是我得到一个错误: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;

非常感谢任何帮助。

20 个答案:

答案 0 :(得分:232)

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),则不得在(a,c,b)的列上定义父PK。

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

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

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

  7. 如果Child表中已存在数据,则FK列中的每个值都必须与父表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;被用于错误定义的不一致关系。请参阅下面的@ andrewdotn的答案,了解如何识别这些问题FK的说明。

  13. 希望这有帮助。

答案 1 :(得分:61)

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上定义主键,并成功创建外键约束。

答案 2 :(得分:25)

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

通常,ID列上的'unsigned'属性会让你感到沮丧。

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

答案 3 :(得分:10)

运行此脚本时数据库的当前状态是什么?它完全是空的吗?从头开始创建数据库时,你的SQL运行正常,但是errno 150通常与drop&重新创建属于外键的表。我感觉你没有使用100%全新的数据库。

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

您也可以查看手册:

  

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

     

- MySQL 5.1 reference manual

答案 4 :(得分:5)

对于使用相同问题查看此主题的人:

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

MySQL Foreign Key Errors and Errno 150

答案 5 :(得分:4)

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

答案 6 :(得分:3)

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

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

答案 7 :(得分:3)

绝对不是这样,但我发现这个错误非常普遍且不明显。 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 :(得分:2)

请先确认

  1. 您正在使用InnoDB表。
  2. FOREIGN KEY的
  3. 字段与源字段具有相同的类型和长度(!)。
  4. 我遇到了同样的麻烦,我已经修好了。我有一个字段的无符号INT和其他字段的整数。

答案 9 :(得分: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)                                                                                                                                                                           |
+---------+------+--------------------------------------------------------------------------                          --------------------------------------------------------------------------------------------                          ---------------+

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

答案 10 :(得分:1)

我发现了这个失败的另一个原因......区分大小写的表名。

对于此表定义

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上失败的事实花了我几个小时才弄明白。希望能帮助别人。

答案 11 :(得分:1)

适用于Mac OS的MySQL Workbench 6.3。

问题:当在DB图上尝试进行正向工程时,表X上的错误150,21个中的20个成功,1个失败。如果删除了表X上的FK,则错误会移至另一个之前未发生故障的表。

将所有表格引擎更改为myISAM并且工作正常。

enter image description here

答案 12 :(得分:1)

当您尝试将文件导入现有数据库时,通常会发生这种情况。 首先删除所有表(或DB本身)。 然后是源文件,开头是SET foreign_key_checks = 0;,最后是SET foreign_key_checks = 1;

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

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

答案 15 :(得分:0)

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

答案 16 :(得分:0)

另外值得检查的是,您不会意外地在错误的数据库上运行。如果外部表不存在,则会发生此错误。为什么MySQL必须如此神秘?

答案 17 :(得分:0)

(旁注对评论来说太大了)

映射表中不需要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 :(得分:0)

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

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

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

SET FOREIGN_KEY_CHECKS = 1;

-不检查外键约束

SET FOREIGN_KEY_CHECKS = 0;

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

答案 19 :(得分:-1)

我遇到了同样的问题,但是我检查发现我没有父表。因此,我只在子迁移之前编辑父迁移。随便吧。