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
symbol
symbol
子句中的表和列标识符FOREIGN KEY ...
REFERENCES
可以用反引号 ( `
) 括起来。或者,如果启用了 SQL 模式,则"
可以使用
双引号 ( )。ANSI_QUOTES
系统
lower_case_table_names
变量设置也被考虑在内。
外键约束受以下条件和限制:
父表和子表必须使用相同的存储引擎,不能定义为临时表。
创建外键约束需要
REFERENCES
对父表的特权。外键和引用键中的对应列必须具有相似的数据类型。和等 固定精度类型的大小和符号 必须相同
INTEGER
DECIMAL
。字符串类型的长度不必相同。对于非二进制(字符)字符串列,字符集和排序规则必须相同。MySQL 支持表中一列与另一列之间的外键引用。(列不能有对其自身的外键引用。)在这些情况下, “子表记录”指的是同一表中的从属记录。
MySQL 需要在外键和引用键上建立索引,这样外键检查就可以很快,而不需要表扫描。在引用表中,必须有一个索引,其中外键列以相同的顺序列为 第一列。如果引用表不存在,则会自动在引用表上创建此类索引。如果您创建另一个可用于强制外键约束的索引,则稍后可能会静默删除该索引。
index_name
,如果给定,则如前所述使用。InnoDB
允许外键引用任何索引列或列组。但是,在被引用的表中,必须有一个索引,其中被引用的列是相同顺序的第一 列。InnoDB
添加到索引的隐藏列 也被考虑在内(请参阅第 14.6.2.1 节,“聚簇索引和二级索引”)。NDB
需要在任何引用为外键的列上有一个显式唯一键(或主键)。InnoDB
没有,这是标准 SQL 的扩展。InnoDB
目前不支持具有用户定义分区的表的外键。这包括父表和子表。此限制不适用于 按or
NDB
分区的表(存储引擎支持的唯一用户分区类型);这些可能具有外键引用或者是此类引用的目标。KEY
LINEAR KEY
NDB
不能更改外键关系中的表以使用其他存储引擎。要更改存储引擎,您必须先删除任何外键约束。
外键约束不能引用虚拟生成的列。
在 5.7.16 之前,外键约束不能引用在虚拟生成列上定义的二级索引。
有关外键约束的 MySQL 实现与 SQL 标准有何不同的信息,请参阅 第 1.7.2.3 节,“外键约束差异”。
当UPDATE
or
DELETE
操作影响子表中具有匹配行的父表中的键值时,结果取决于子句
和子句
指定的引用操作。参考行动包括:
ON UPDATE
ON DELETE
FOREIGN KEY
CASCADE
:删除或更新父表中的行,并自动删除或更新子表中匹配的行。ON DELETE CASCADE
和均受ON UPDATE CASCADE
支持。两个表之间,不要定义多个ON UPDATE CASCADE
作用于父表或子表中同一列的子句。如果
FOREIGN KEY
在外键关系中的两个表上都定义了一个子句,使两个表成为父表和子表,则必须为另一个子句 定义为一个子句定义的ON UPDATE CASCADE
or 子句,以便级联操作成功。如果 只为一个子句定义了or子句,则级联操作会失败并出现错误。ON DELETE CASCADE
FOREIGN KEY
ON UPDATE CASCADE
ON DELETE CASCADE
FOREIGN KEY
笔记级联的外键操作不会激活触发器。
SET NULL
:删除或更新父表中的行,并将子表中的外键列设置为NULL
。ON DELETE SET NULL
和ON UPDATE SET NULL
子句均受支持 。如果您指定一个
SET NULL
操作, 请确保您没有将子表中的列声明为NOT NULL
。RESTRICT
:拒绝对父表的删除或更新操作。指定RESTRICT
(orNO ACTION
) 与省略ON DELETE
orON UPDATE
子句相同。NO ACTION
: 来自标准 SQL 的关键字。在 MySQL 中,相当于RESTRICT
. 如果被引用表中存在相关的外键值,MySQL Server 将拒绝对父表的删除或更新操作。有些数据库系统有延期检查,而且NO ACTION
是延期检查。在 MySQL 中,立即检查外键约束,因此NO ACTION
与RESTRICT
.SET DEFAULT
:此操作可被 MySQL 解析器识别,但同时InnoDB
拒绝NDB
包含ON DELETE SET DEFAULT
orON UPDATE SET DEFAULT
子句的表定义。
对于支持外键的存储引擎,如果父表中没有匹配的候选键值,MySQL 将拒绝任何试图在子表中创建外键值的操作
INSERT
。
UPDATE
对于未指定的ON DELETE
or ON
UPDATE
,默认操作始终为RESTRICT
。
对于NDB
表,ON
UPDATE CASCADE
不支持引用父表的主键。
从 NDB 7.5.14 和 NDB 7.6.10 开始:对于
NDB
表,ON DELETE
CASCADE
不支持子表包含任何类型的一个或多个列的
TEXT
情况
BLOB
。(漏洞 #89511,漏洞 #27484882)
InnoDB
对外键约束对应的索引的记录使用深度优先搜索算法进行级联操作。
存储生成列的外键约束不能使用CASCADE
、SET NULL
或SET DEFAULT
作为ON
UPDATE
引用操作,也不能使用
SET NULL
或SET DEFAULT
作为ON DELETE
引用操作。
存储生成列的基列上的外键约束不能使用CASCADE
、
SET NULL
、 或SET DEFAULT
asON UPDATE
或ON
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. Disablingforeign_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 whileforeign_key_checks
is disabled are not checked for consistency whenforeign_key_checks
is re-enabled.
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
当涉及到
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
错误消息会公开有关父表的信息,即使用户没有父表访问权限也是如此。要隐藏有关父表的信息,请在应用程序代码和存储程序中包含适当的条件处理程序。