Documentation Home

13.1.18.5 外键约束

MySQL 支持外键,允许跨表交叉引用相关数据,以及外键约束,这有助于保持相关数据的一致性。

外键关系涉及包含初始列值的父表,以及包含引用父列值的列值的子表。在子表上定义了外键约束。

CREATE TABLE在or 语句 中定义外键约束的基本语法ALTER TABLE包括以下内容:

[CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (col_name, ...)
    REFERENCES tbl_name (col_name,...)
    [ON DELETE reference_option]
    [ON UPDATE reference_option]

reference_option:
    RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT

本节的以下主题描述了外键约束的使用:

身份标识

外键约束命名遵循以下规则:

  • 如果已定义,则使用该CONSTRAINT symbol值。

  • 如果CONSTRAINT symbol子句未定义,或 CONSTRAINT关键字后未包含符号:

    • 对于InnoDB表,约束名称是自动生成的。

    • 对于NDB表, FOREIGN KEY index_name如果已定义,则使用该值。否则,将自动生成约束名称。

  • 该值(如果已定义)在数据库中必须是唯一的。重复 导致错误类似于:ERROR 1005 (HY000): Can't create table 'test.fk1' (errno: 121)CONSTRAINT symbolsymbol

子句中的表和列标识符FOREIGN KEY ... REFERENCES可以用反引号 ( `) 括起来。或者,如果启用了 SQL 模式,则"可以使用 双引号 ( )。ANSI_QUOTES系统 lower_case_table_names变量设置也被考虑在内。

条件和限制

外键约束受以下条件和限制:

  • 父表和子表必须使用相同的存储引擎,不能定义为临时表。

  • 创建外键约束需要 REFERENCES对父表的特权。

  • 外键和引用键中的对应列必须具有相似的数据类型。和等 固定精度类型的大小和符号 必须相同INTEGERDECIMAL。字符串类型的长度不必相同。对于非二进制(字符)字符串列,字符集和排序规则必须相同。

  • MySQL 支持表中一列与另一列之间的外键引用。(列不能有对其自身的外键引用。)在这些情况下, 子表记录指的是同一表中的从属记录。

  • MySQL 需要在外键和引用键上建立索引,这样外键检查就可以很快,而不需要表扫描。在引用表中,必须有一个索引,其中外键列以相同的顺序列为 第一列。如果引用表不存在,则会自动在引用表上创建此类索引。如果您创建另一个可用于强制外键约束的索引,则稍后可能会静默删除该索引。 index_name,如果给定,则如前所述使用。

  • InnoDB允许外键引用任何索引列或列组。但是,在被引用的表中,必须有一个索引,其中被引用的列是相同顺序的第一 列。InnoDB添加到索引的隐藏列 也被考虑在内(请参阅第 14.6.2.1 节,“聚簇索引和二级索引”)。

    NDB需要在任何引用为外键的列上有一个显式唯一键(或主键)。 InnoDB没有,这是标准 SQL 的扩展。

  • 不支持外键列的索引前缀。因此,BLOBand TEXT列不能包含在外键中,因为这些列上的索引必须始终包含前缀长度。

  • InnoDB目前不支持具有用户定义分区的表的外键。这包括父表和子表。

    此限制不适用于 按orNDB分区的表(存储引擎支持的唯一用户分区类型);这些可能具有外键引用或者是此类引用的目标。 KEYLINEAR KEYNDB

  • 不能更改外键关系中的表以使用其他存储引擎。要更改存储引擎,您必须先删除任何外键约束。

  • 外键约束不能引用虚拟生成的列。

  • 在 5.7.16 之前,外键约束不能引用在虚拟生成列上定义的二级索引。

有关外键约束的 MySQL 实现与 SQL 标准有何不同的信息,请参阅 第 1.7.2.3 节,“外键约束差异”

参考行动

UPDATEor DELETE操作影响子表中具有匹配行的父表中的键值时,结果取决于子句 和子句 指定的引用操作。参考行动包括: ON UPDATEON DELETEFOREIGN KEY

  • CASCADE:删除或更新父表中的行,并自动删除或更新子表中匹配的行。ON DELETE CASCADE和均受ON UPDATE CASCADE 支持。两个表之间,不要定义多个 ON UPDATE CASCADE作用于父表或子表中同一列的子句。

    如果FOREIGN KEY在外键关系中的两个表上都定义了一个子句,使两个表成为父表和子表,则必须为另一个子句 定义为一个子句定义的ON UPDATE CASCADEor 子句,以便级联操作成功。如果 只为一个子句定义了or子句,则级联操作会失败并出现错误。 ON DELETE CASCADEFOREIGN KEYON UPDATE CASCADEON DELETE CASCADEFOREIGN KEY

    笔记

    级联的外键操作不会激活触发器。

  • SET NULL:删除或更新父表中的行,并将子表中的外键列设置为NULLON DELETE SET NULLON UPDATE SET NULL子句均受支持 。

    如果您指定一个SET NULL操作, 请确保您没有将子表中的列声明为NOT NULL

  • RESTRICT:拒绝对父表的删除或更新操作。指定 RESTRICT(or NO ACTION) 与省略ON DELETEorON UPDATE子句相同。

  • NO ACTION: 来自标准 SQL 的关键字。在 MySQL 中,相当于RESTRICT. 如果被引用表中存在相关的外键值,MySQL Server 将拒绝对父表的删除或更新操作。有些数据库系统有延期检查,而且NO ACTION是延期检查。在 MySQL 中,立即检查外键约束,因此NO ACTIONRESTRICT.

  • SET DEFAULT:此操作可被 MySQL 解析器识别,但同时 InnoDB拒绝 NDB包含ON DELETE SET DEFAULTor ON UPDATE SET DEFAULT子句的表定义。

对于支持外键的存储引擎,如果父表中没有匹配的候选键值,MySQL 将拒绝任何试图在子表中创建外键值的操作 INSERTUPDATE

对于未指定的ON DELETEor ON UPDATE,默认操作始终为RESTRICT

对于NDB表,ON UPDATE CASCADE不支持引用父表的主键。

从 NDB 7.5.14 和 NDB 7.6.10 开始:对于 NDB表,ON DELETE CASCADE不支持子表包含任何类型的一个或多个列的 TEXT情况 BLOB。(漏洞 #89511,漏洞 #27484882)

InnoDB对外键约束对应的索引的记录使用深度优先搜索算法进行级联操作。

存储生成列的外键约束不能使用CASCADESET NULLSET DEFAULT作为ON UPDATE引用操作,也不能使用 SET NULLSET DEFAULT 作为ON DELETE引用操作。

存储生成列的基列上的外键约束不能使用CASCADESET NULL、 或SET DEFAULT asON UPDATEON DELETE引用操作。

在 MySQL 5.7.13 及更早版本中,InnoDB不允许在 索引虚拟生成列的基列上定义具有级联引用操作的外键约束。MySQL 5.7.14 中取消了此限制。

在 MySQL 5.7.13 及更早版本中,InnoDB不允许在显式包含在虚拟索引中的非虚拟外键列上定义级联引用操作。MySQL 5.7.14 中取消了此限制。

外键约束示例

这个简单的例子通过单列外键 关联parent和 表:child

CREATE TABLE parent (
    id INT NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id INT,
    parent_id INT,
    INDEX par_ind (parent_id),
    FOREIGN KEY (parent_id)
        REFERENCES parent(id)
        ON DELETE CASCADE
) ENGINE=INNODB;

这是一个更复杂的示例,其中一个 product_order表具有其他两个表的外键。一个外键引用表中的两列索引product。另一个引用表中的单列索引 customer

CREATE TABLE product (
    category INT NOT NULL, id INT NOT NULL,
    price DECIMAL,
    PRIMARY KEY(category, id)
)   ENGINE=INNODB;

CREATE TABLE customer (
    id INT NOT NULL,
    PRIMARY KEY (id)
)   ENGINE=INNODB;

CREATE TABLE product_order (
    no INT NOT NULL AUTO_INCREMENT,
    product_category INT NOT NULL,
    product_id INT NOT NULL,
    customer_id INT NOT NULL,

    PRIMARY KEY(no),
    INDEX (product_category, product_id),
    INDEX (customer_id),

    FOREIGN KEY (product_category, product_id)
      REFERENCES product(category, id)
      ON UPDATE CASCADE ON DELETE RESTRICT,

    FOREIGN KEY (customer_id)
      REFERENCES customer(id)
)   ENGINE=INNODB;
添加外键约束

ALTER TABLE 您可以使用以下语法 向现有表添加外键约束:

ALTER TABLE tbl_name
    ADD [CONSTRAINT [symbol]] FOREIGN KEY
    [index_name] (col_name, ...)
    REFERENCES tbl_name (col_name,...)
    [ON DELETE reference_option]
    [ON UPDATE reference_option]

外键可以是自引用的(引用同一个表)。当您使用 向表添加外键约束时 ALTER TABLE请记住首先在外键引用的列上创建索引。

删除外键约束

您可以使用以下 ALTER TABLE语法删除外键约束:

ALTER TABLE tbl_name DROP FOREIGN KEY fk_symbol;

如果在创建约束时该FOREIGN KEY子句定义了 CONSTRAINT名称,则可以引用该名称来删除外键约束。否则,会在内部生成约束名称,您必须使用该值。要确定外键约束名称,请使用SHOW CREATE TABLE

mysql> SHOW CREATE TABLE child\G
*************************** 1. row ***************************
       Table: child
Create Table: CREATE TABLE `child` (
  `id` int(11) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  KEY `par_ind` (`parent_id`),
  CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`)
  REFERENCES `parent` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1

mysql> ALTER TABLE child DROP FOREIGN KEY `child_ibfk_1`;

ALTER TABLE支持 在同一语句中添加和删除外键 ALTER TABLE ... ALGORITHM=INPLACE。不支持它 ALTER TABLE ... ALGORITHM=COPY

外键检查

In MySQL, InnoDB and NDB tables support checking of foreign key constraints. Foreign key checking is controlled by the foreign_key_checks variable, which is enabled by default. Typically, you leave this variable enabled during normal operation to enforce referential integrity. The foreign_key_checks variable has the same effect on NDB tables as it does for InnoDB tables.

The foreign_key_checks variable is dynamic and supports both global and session scopes. For information about using system variables, see Section 5.1.8, “Using System Variables”.

Disabling foreign key checking is useful when:

  • Dropping a table that is referenced by a foreign key constraint. A referenced table can only be dropped after foreign_key_checks is disabled. When you drop a table, constraints defined on the table are also dropped.

  • Reloading tables in different order than required by their foreign key relationships. For example, mysqldump produces correct definitions of tables in the dump file, including foreign key constraints for child tables. To make it easier to reload dump files for tables with foreign key relationships, mysqldump automatically includes a statement in the dump output that disables foreign_key_checks. This enables you to import the tables in any order in case the dump file contains tables that are not correctly ordered for foreign keys. Disabling foreign_key_checks also speeds up the import operation by avoiding foreign key checks.

  • Executing LOAD DATA operations, to avoid foreign key checking.

  • Performing an ALTER TABLE operation on a table that has a foreign key relationship.

When foreign_key_checks is disabled, foreign key constraints are ignored, with the following exceptions:

  • Recreating a table that was previously dropped returns an error if the table definition does not conform to the foreign key constraints that reference the table. The table must have the correct column names and types. It must also have indexes on the referenced keys. If these requirements are not satisfied, MySQL returns Error 1005 that refers to errno: 150 in the error message, which means that a foreign key constraint was not correctly formed.

  • Altering a table returns an error (errno: 150) if a foreign key definition is incorrectly formed for the altered table.

  • Dropping an index required by a foreign key constraint. The foreign key constraint must be removed before dropping the index.

  • Creating a foreign key constraint where a column references a nonmatching column type.

Disabling foreign_key_checks has these additional implications:

  • It is permitted to drop a database that contains tables with foreign keys that are referenced by tables outside the database.

  • It is permitted to drop a table with foreign keys referenced by other tables.

  • Enabling foreign_key_checks does not trigger a scan of table data, which means that rows added to a table while foreign_key_checks is disabled are not checked for consistency when foreign_key_checks is re-enabled.

Foreign Key Definitions and Metadata

To view a foreign key definition, use SHOW CREATE TABLE:

mysql> SHOW CREATE TABLE child\G
*************************** 1. row ***************************
       Table: child
Create Table: CREATE TABLE `child` (
  `id` int(11) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  KEY `par_ind` (`parent_id`),
  CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`)
  REFERENCES `parent` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1

You can obtain information about foreign keys from the INFORMATION_SCHEMA.KEY_COLUMN_USAGE table. An example of a query against this table is shown here:

mysql> SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, CONSTRAINT_NAME
       FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
       WHERE REFERENCED_TABLE_SCHEMA IS NOT NULL;
+--------------+------------+-------------+-----------------+
| TABLE_SCHEMA | TABLE_NAME | COLUMN_NAME | CONSTRAINT_NAME |
+--------------+------------+-------------+-----------------+
| test         | child      | parent_id   | child_ibfk_1    |
+--------------+------------+-------------+-----------------+

You can obtain information specific to InnoDB foreign keys from the INNODB_SYS_FOREIGN and INNODB_SYS_FOREIGN_COLS tables. Example queries are show here:

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN \G
*************************** 1. row ***************************
      ID: test/child_ibfk_1
FOR_NAME: test/child
REF_NAME: test/parent
  N_COLS: 1
    TYPE: 1

mysql> SELECT * FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN_COLS \G
*************************** 1. row ***************************
          ID: test/child_ibfk_1
FOR_COL_NAME: parent_id
REF_COL_NAME: id
         POS: 0
Foreign Key Errors

当涉及到 InnoDB表的外键错误时(MySQL Server一般是Error 150),可以通过查看 SHOW ENGINE INNODB STATUS输出获取最新的外键错误信息。

mysql> SHOW ENGINE INNODB STATUS\G
...
------------------------
LATEST FOREIGN KEY ERROR
------------------------
2014-10-16 18:35:18 0x7fc2a95c1700 Transaction:
TRANSACTION 1814, ACTIVE 0 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 3
MySQL thread id 2, OS thread handle 140474041767680, query id 74 localhost
root update
INSERT INTO child VALUES
    (NULL, 1)
    , (NULL, 2)
    , (NULL, 3)
    , (NULL, 4)
    , (NULL, 5)
    , (NULL, 6)
Foreign key constraint fails for table `mysql`.`child`:
,
  CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent`
  (`id`) ON DELETE CASCADE ON UPDATE CASCADE
Trying to add in child table, in index par_ind tuple:
DATA TUPLE: 2 fields;
 0: len 4; hex 80000003; asc     ;;
 1: len 4; hex 80000003; asc     ;;

But in parent table `mysql`.`parent`, in index PRIMARY,
the closest match we can find is record:
PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 4; hex 80000004; asc     ;;
 1: len 6; hex 00000000070a; asc       ;;
 2: len 7; hex aa0000011d0134; asc       4;;
...
警告

ER_NO_REFERENCED_ROW_2外键操作的 ER_ROW_IS_REFERENCED_2错误消息会公开有关父表的信息,即使用户没有父表访问权限也是如此。要隐藏有关父表的信息,请在应用程序代码和存储程序中包含适当的条件处理程序。