24

我在 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 类型的键,我不确定这意味着什么)。但是,当我尝试输入 questions 或 users 表中不存在的 Q_id 或 U_id 时,MySQL 仍然允许这些值。

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

更新 1

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

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

为什么会发生这种情况?

更新 2

有人告诉我,用功能外键强制数据完整性很重要,而且 InnoDB 不应该与 MySQL 一起使用。你有什么建议吗?

4

10 回答 10

58

我猜你的默认存储引擎是 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 比 MyISAM 具有更好的性能(尽管 InnoDB 需要更多注意调整配置),InnoDB 支持原子更改、事务、外键,并且 InnoDB 在崩溃时更能抵抗损坏数据。

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

于 2008-12-19T04:02:23.723 回答
9

只是为了节省其他人的头痛时间-正如长颈鹿所涉及的那样,确保@FOREIGN_KEY_CHECKS 设置为1。

选择@@FOREIGN_KEY_CHECKS

设置 FOREIGN_KEY_CHECKS=1

于 2011-10-20T22:47:21.113 回答
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”为我创造了魔力。

我希望这对其他人有用。祝你好运。

于 2010-04-01T15:58:51.177 回答
2

对于那些忽略外键约束的 mysql 仍然存在问题的人,以及以上或任何其他相关问题的答案没有解决问题的人,这就是我发现的问题。

如果你这样声明你的外键

id  INTEGER UNSIGNED    REFERENCES    A_Table(id)

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

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

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

于 2013-06-30T10:18:40.387 回答
1

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

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

作者 Edwin Dando 说:

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

于 2008-12-19T04:20:00.067 回答
1

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

(1) 通过将“ENGINE=InnoDB”附加到“CREATE TABLE”语句中,将表更改为 InnoDB。

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

(2) 确保通过执行“SET foreign_key_checks = 'ON'”来检查外键约束

(3) 将“ON UPDATE CASCADE”附加到您的外键声明中。

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

于 2014-06-01T09:01:20.763 回答
1

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

于 2008-12-19T14:59:23.610 回答
0

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

在我尝试创建外键约束的情况下,我只遇到了“无法创建表”,而我的本地列与外列的类型不同。

于 2008-12-19T04:27:51.220 回答
0

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

正向工程 - 选项部分

于 2014-09-17T11:25:00.720 回答
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;
于 2011-09-20T20:50:01.333 回答