外键在MySQL中不起作用:为什么我可以插入不在外部列中的值?

时间:2008-12-19 03:51:05

标签: mysql foreign-keys innodb mysql-error-1005

我在MySQL中创建了一个表:

CREATE TABLE actions ( A_id int NOT NULL AUTO_INCREMENT,
type ENUM('rate','report','submit','edit','delete') NOT NULL,
Q_id int NOT NULL,
U_id int NOT NULL,
date DATE NOT NULL,
time TIME NOT NULL,
rate tinyint(1),
PRIMARY KEY (A_id),
CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id));

这创建了我想要的表(虽然“DESCRIBE actions;”命令向我显示外键是MUL类型的键,我不确定这意味着什么)。但是,当我尝试输入问题或用户表中不存在的Q_id或U_id时, MySQL仍允许这些值。

我做错了什么?如何防止带有外键的表接受无效数据?

更新1

如果我将TYPE=InnoDB添加到最后,我会收到错误:

  

ERROR 1005(HY000):无法创建表'./quotes/actions.frm'(错误号:150)

为什么会发生这种情况?

更新2

我被告知使用功能外键强制数据完整性非常重要,但InnoDB也不应该与MySQL一起使用。你推荐什么?

10 个答案:

答案 0 :(得分:52)

我猜你的默认存储引擎是MyISAM,它会忽略外键约束。它默默地接受外键的声明,但不存储约束或随后强制执行。

但是,它会隐式地为您为外键声明的列创建索引。在MySQL中,“KEY”是“INDEX”的同义词。这就是DESCRIBE输出中显示的内容:索引,但不是约束。

您现在可以向表中插入无效值,因为没有约束。要获得强制引用完整性的约束,必须使用InnoDB存储引擎:

CREATE TABLE actions (
  A_id int NOT NULL AUTO_INCREMENT,
  ...
  CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
  CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id)
) ENGINE=InnoDB;

我一直认为,对于默默地忽略外键约束声明,MySQL的一个大错误。没有错误或警告存储引擎不支持它们。

CHECK约束也是如此。顺便说一下,MySQL使用的存储引擎不支持CHECK约束,但SQL解析器接受它们没有任何抱怨。


当它无法创建InnoDB表时会出现errno 150问题,因为它无法理解外键约束。您可以通过以下方式获得更多信息:

SHOW ENGINE INNODB STATUS;

InnoDB外键的一些要求:

  • 引用表也必须是InnoDB。
  • 引用表必须具有索引和主键。
  • FK列和引用的PK列的SQL数据类型必须相同。例如,INT与BIGINT或INT UNSIGNED不匹配。

您可以更改包含数据的表格的存储引擎:

ALTER TABLE actions ENGINE=InnoDB;

这有效地将整个MyISAM表复制到InnoDB表,然后一旦成功,它就会删除MyISAM表并将新的InnoDB表重命名为以前的MyISAM表的名称。这称为“表重组”,它可能非常耗时,具体取决于表中的数据量。在ALTER TABLE期间发生表重组,即使在某些情况下似乎也没有必要。


重新更新2:

  

我被告知使用功能外键强制数据完整性非常重要,但InnoDB也不应该与MySQL一起使用。你推荐什么?

谁告诉过你的?这绝对是假的。 InnoDB has better performance than MyISAM(尽管InnoDB需要更多关注tuning the configuration),InnoDB支持原子更改,事务,外键,而InnoDB更能抵御崩溃中的数据损坏。

除非您运行旧的,不受支持的MySQL版本(5.0或更早版本),否则您应该使用InnoDB作为默认存储引擎选项,并且只有在您可以演示特定工作负载时才使用MyISAM受益于MyISAM。

答案 1 :(得分:6)

我知道很久以前这个帖子已经打开了,但是我发布这条消息给未来的用户,他们会寻找答案。 我在mysql中遇到了与外键相同的问题。 以下事情对我有用。

父表:

CREATE TABLE NameSubject (
  Autonumber INT NOT NULL AUTO_INCREMENT,
  NameorSubject nvarchar(255),
  PRIMARY KEY (Autonumber)
 ) ENGINE=InnoDB;

子表:

CREATE TABLE Volumes (
  Autonumber INT NOT NULL,
  Volume INT,
  Pages nvarchar(50),
  Reel int,
  Illustrations bit,
  SSMA_TimeStamp timestamp,
  Foreign KEY (Autonumber) references NameSubject(Autonumber)
  ON  update cascade 
)engine=innodb;

“ON update cascade”为我带来了魔力。

我希望这适用于其他人。祝你好运。

答案 2 :(得分:6)

为了节省其他时间的头痛我已经过了 - 当giraffa触及时,确保@FOREIGN_KEY_CHECKS设置为1.

SELECT @@ FOREIGN_KEY_CHECKS

SET FOREIGN_KEY_CHECKS = 1

答案 3 :(得分:1)

我找到了以下文章。我目前没有时间对其进行测试,但它可能会有所帮助:

http://forums.mysql.com/read.php?22,19755,43805

作者Edwin Dando说:

  

这两个表必须是INNODB。外键字段必须有一个索引。 foeign键字段和被引用的字段必须是相同的类型(我只使用整数),并且在经过数小时的痛苦之后,它们必须是UNSIGNED。

答案 4 :(得分:1)

问题很可能是questions.p_id和users.p_id未定义为INT NOT NULL。要使外键工作,外键两侧的列的定义必须完全匹配,auto_increment和default除外。

答案 5 :(得分:1)

如果我先看到它,这个答案可以为我节省很多时间。尝试以下三个步骤,我按照新手错误的频率命令:

(1)通过附加" ENGINE = InnoDB"将表更改为InnodDB。到你的" CREATE TABLE"语句。

其他引擎(可能是默认引擎)不支持外键约束,但它们也不会抛出错误或警告,告诉您它们不受支持。

(2)确保通过执行" SET foreign_key_checks =' ON'"

确保外键约束被检查

(3)追加" ON UPDATE CASCADE"你的外国钥匙声明。

注意:确保级联是您想要的行为。还有其他选择...

答案 6 :(得分:0)

如上所述,您的表必须是强制执行FK约束的InnoDB。

在我尝试创建外键约束的情况下,我只遇到'无法创建表',其中我的本地列与外来列的类型不同。

答案 7 :(得分:0)

我认为一些有这个问题的人可能会开始使用ORACLE网站上为MYSQL提供的一些示例数据库(例如sakila DB)。不要忘记在脚本末尾“重新启用外键约束”(例如,在sakila DB脚本开头关闭它们)

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

在这里创建表格

然后不要忘记这一点:

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

答案 8 :(得分:0)

对于那些仍然遇到mysql问题忽略外键约束的人,​​对于那些上述答案或任何其他相关问题无法解决teir拼图的人,我发现这就是问题所在。

如果您声明外键

id  INTEGER UNSIGNED    REFERENCES    A_Table(id)

然后外键似乎被忽略,为了强制执行约束而没有(显然)必须使用任何SET命令,请使用以下声明。

id  INTEGER UNSIGNED,
CONSTRAINT fk_id  FOREIGN KEY (id)  REFERENCES A_Table(id)

这种方式为我解决了问题。不知道为什么,正如许多人所说,第一个声明只是第二个变体的简写。

答案 9 :(得分:0)

好吧,我的猜测是以某种方式检查了“跳过创建FOREIGN KEYS”选项,它可能发生在“正向工程”过程的“选项”部分。

Forward Engineering - Options section