50

我在这里查看 MySQL 文档并试图理清 FOREIGN KEYs 和 CONSTRAINTs 之间的区别。我认为 FK一种约束,但文档似乎在谈论它们,就像它们是独立的东西一样。

创建 FK 的语法是(部分)...

[CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (index_col_name, ...)
    REFERENCES tbl_name (index_col_name,...)

所以“CONSTRAINT”子句是可选的。为什么要包含它或不包含它?如果你忽略它,MySQL会创建一个外键而不是一个约束吗?或者它更像是“CONSTRAINT”只不过是你的 FK 的名称,所以如果你不指定它,你会得到一个匿名的 FK?

任何澄清将不胜感激。

谢谢,

伊森

4

7 回答 7

61

是的,外键是一种约束。MySQL 对约束的支持不均衡:

  • PRIMARY KEY:是的,作为表约束和列约束。
  • FOREIGN KEY:是的,作为表约束,但仅适用于 InnoDB 和 BDB 存储引擎;否则被解析但被忽略。
  • CHECK: 在所有存储引擎中被解析但被忽略。
  • UNIQUE:是的,作为表约束和列约束。
  • NOT NULL:是的,作为列约束。
  • DEFERRABLE和其他约束属性:不支持。

CONSTRAINT子句允许您显式命名约束,以使元数据更具可读性,或者在您想要删除约束时使用该名称。SQL 标准要求CONSTRAINT子句是可选的。如果您忽略它,RDBMS 会自动创建一个名称,该名称由实现决定。

于 2008-11-21T23:58:04.880 回答
9

一般来说(不是必须的 MySQL),外键是约束,但约束并不总是外键。考虑主键约束、唯一约束等。

回到具体问题,你是对的,省略 CONSTRAINT [symbol] 部分将创建一个具有自动生成名称的 FK。

于 2008-11-22T00:01:51.953 回答
4

到目前为止,我们的 CREATE TABLE DDL 是这种格式 - 请注意我们使用的 UNIQUE KEY 和 FOREIGN KEY 定义语法。

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    UNIQUE KEY uk1_my_table(account_nbr, account_name),
    FOREIGN KEY fk1_my_table(vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

在这种格式中,MySQL 会自动创建名为 uk1_my_table 和 fk1_my_table 的 INDEX-es;但 FK 对象名称有所不同 - my_table_ibfk_1(即 tablename_ibfk_N – 系统定义)。所以ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table不起作用(因此令人沮丧并发出警报),因为没有该名称的 FK db 对象。

这是带有约束的替代 DDL 格式(参考: https : //dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html ):-

CREATE TABLE my_dbschema.my_table (
    id INT unsigned auto_increment PRIMARY KEY,
    account_nbr INT NOT NULL,
    account_name VARCHAR(50) NOT NULL,
    active_flg CHAR(1) NOT NULL DEFAULT 'Y',
    vendor_nbr INT NOT NULL,
    create_ts TIMESTAMP NOT NULL DEFAULT current_timestamp,
    create_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    last_upd_ts TIMESTAMP NOT NULL DEFAULT current_timestamp ON UPDATE current_timestamp,
    last_upd_usr_id VARCHAR(10) NOT NULL DEFAULT 'DFLTUSR',
    CONSTRAINT uk1_my_table UNIQUE KEY (account_nbr, account_name),
    CONSTRAINT fk1_my_table FOREIGN KEY (vendor_nbr) REFERENCES vendor(vendor_nbr)
    );

在这种格式中,MySQL 仍在自动创建名称为 uk1_my_table 和 fk1_my_table 的 INDEX-es,但 FK 对象名称并没有什么不同——它是 DDL 中提到的 fk1_my_table。如此ALTER TABLE my_table DROP FOREIGN KEY fk1_my_table有效,但留下了同名的 INDEX。

并且,请注意ALTER TABLE my_table DROP INDEX fk1_my_table最初不起作用(当 FK 尚未删除时),并显示一条错误消息,表明它正在 FK 中使用!如果 DROP FK 命令已成功执行,则只有 DROP INDEX 起作用。

希望这能解释并帮助解决困惑。

于 2015-12-15T20:00:02.707 回答
1

无法回答 MySQL 但 FK 是约束。任何迫使您的数据进入特定条件的东西都是一种约束。约束有好几种,Unique、Primary Key、Check和Foreign Keys都是约束。也许 MySQL 有其他的。

有时,命令中允许使用单词,但并不像 DELETE 语句中的 FROM 那样纯粹为了可读性而需要单词。

于 2008-11-21T23:47:45.570 回答
1

如果我没记错的话,约束需要索引,因此,例如,当您创建外键约束时,MySQL 也会自动创建索引。

于 2011-04-25T19:04:31.507 回答
1

我将在这里大吃一惊,虽然我实际上不知道我的答案是否准确,所以如果您了解数据库工程的内部内容,请纠正我。但如果我是对的,我认为这会有所帮助。

外键及其关联的外键约束不是一回事,就像汽车发动机和曲轴不是一回事一样。发动机将汽油的爆炸转化为直线运动(活塞),曲轴将直线运动转化为转动运动,然后转动汽车的车轮。发动机和曲轴一起使汽车行驶。

同样,外键和外键约束不是一回事,但它们共同创造了“外键关系”的概念。

定义:

“外键”是外键索引的缩写。

“约束”是外键约束的缩写。

索引和约束共同构成“外键关系”。

外键关系要求子表中的值存在于其父表中,从而确保数据库中的数据完整性。

因为Key是Index的意思,所以我们不说“外键索引”。我们只说外键,但不说“索引”是造成很多混乱的原因。

创建外键(外键索引)会创建二叉搜索树(也称为字典,因为树用于查找值)。二叉搜索树(BST)然后存在于计算机内存中并占用物理磁盘空间,但允许从子表到父表的 O(log n) JOIN 访问时间(几乎立即)。

创建外键约束就是创建一个规则,它是一段代码,当您在外键列上处理(INSERT、SELECT 等)时会调用它。约束本质上是一个数据库触发器。约束就像电子邮件过滤器:在某个操作上调用的一段代码,例如 WHEN(新电子邮件)IF(来自:crzy@xgfrnd.com){SEND TO Trash Folder;}。

因此,外键“约束”将是一段被调用的代码(本质上是触发器),看起来像这样:当插入 child_column IF (NOT IN parent_table) DO NOT ALLOW INSERT。

然后你有你的级联和更新和删除规则(约束),以及它们的各种 if / then 条件和操作等......

因此,“外键”是将子表列值映射到父表列值的 BST 字典。外键的目的是速度(不是数据完整性,因为可以在没有索引的情况下实现数据完整性,尽管速度很慢)。

外键约束是一条规则:在 SQL 语句上触发的代码,并且该规则使用 BST 作为快速处理的字典,以避免遍历表,这最终可能会产生类似笛卡尔的行为。外键约束的目的是数据完整性。

我从来没有创建一个父表,其中引用的父列本身不是父表中的键。那么问题来了,外键索引(BST 字典)真的需要吗?约束是肯定需要的,以确保数据完整性,但外键索引(BST 字典)实际上不需要外键规则,因此,“键”和“索引”有两个不同的定义。“Index”是 BST 树,“Key”是规则(子值必须存在于父表中的想法)。然而,在 MySQL 中,需要外键索引,只是因为他们以这种方式对其进行了编程,但他们不必这样做。当父列本身没有索引时,拥有 BST 树会更快。我永远不会建议使引用的父列不是键(索引)。但是,如果有人确实使用没有 BST 的外键约束引用了非索引父列,那么 SQL 操作将逐渐变慢,并且您的应用程序最终可能会陷入困境。

混乱:形容词和动词。

当我们通俗地说“外键”时,我们通常指的是外键关系,而不是外键索引。但是Key这个词的意思是索引。所以这就是所有混乱的根源。即缺乏保留关键字定义标准。在 MySQL CREATE TABLE 语句中,FOREIGN KEY 表示索引(BST),而 CONSTRAINT 命名规则,因此,混淆来自我们说话时短语“外键”与定义的“外键”的差异在实际的 SQL 语句中。

在计算机代码中,“Foreign”是形容词,“Key”是名词,表示索引。

在口语中,“外键”是形容词,“索引”、“约束”、“关系”都是名词。当我们在办公室隔间相互交谈时,“外键”意味着数据完整性的“想法”(即规则,而不是索引)。

不幸的是,程序员总是在寻找简写的打字方式,这常常会引起混乱。计算机科学中的一切都是一种权衡,包括编码风格。

如果创建 MySQL 表的语法改为使用以下保留字,那么混淆就会消失: FOREIGN KEY CONSTRAINT fk1_rule_child_columnFOREIGN KEY INDEX ( fk_bst_child_to_parent_column) REFERENCES parent_table( parent_column)

此外,由于 MySQL 总是同时创建索引和约束,因此 MySQL 创建者可以完全隐藏(抽象)外键关系的双重元素。或者,也许我应该说,他们可以将它们捆绑在一起,这样用户就不必考虑双重方面,而只需创建一个my_foreign_key隐藏双重细节的“外键”。

尽管如此,MySQL 价格低廉、功能强大,而且非常棒。郑重声明,我的抱怨是零,我只对创作者表示感谢。就我而言,他们可以为所欲为。

顺便说一句,作为一种风格推荐,您应该始终将父列和子列命名相同,并且您的表名应该始终包含它们的外键关系。所以你的桌子

customers products attributes orders

应该改为命名

customers products product_attributes customer_product_orders

这样,您和您的继任者只需读取任何表名即可知道外键关系。如果这对您来说打字太多,那么

cust prod prod_attr cust_prod_ord

话虽如此,我猜。我实际上不知道我的 BST 和规则解释是否正确,但我认为它是正确的,希望能解决这个令人困惑的问题。但如果你们这些数据库专家确认、修改、补充或否认我所写的内容,我将不胜感激,如果我弄错了,真正的答案是什么,所以我们最终可以解决这个多代人的谜团. 如果我完全关闭,并且需要删除此答案,那也很好。

于 2021-02-18T18:17:16.603 回答
0

这可能是 MySQL 中最令人困惑的话题。

很多人说,比如'PRIMARY KEY'、'FOREIGN KEY'和'UNIQUE'键实际上是索引!(这里包含 MySQL 官方文档)

另一方面,许多其他人说它们是相当的约束(这确实有道理,因为当您使用它们时,您实际上是在对受影响的列施加限制)。

如果它们确实是索引,那么使用约束子句为其命名有什么意义,因为您应该能够在创建该索引时使用该索引的名称?

例子:

... 外键 index_name (col_name1, col_name2, ...)

如果 FOREIGN KEY 是一个索引,那么我们应该可以使用 index_name 来处理它。但是,我们不能。

但是,如果它们不是索引而是使用索引工作的实际约束,那么这确实是有道理的。

无论如何,我们不知道。实际上,似乎没有人知道。

于 2010-07-11T23:00:19.490 回答