MySQL 对外键约束的实现与 SQL 标准在以下关键方面有所不同
如果父表中有多行具有相同的引用键值,
InnoDB
会执行外键检查,就像其他具有相同键值的父行不存在一样。例如,如果您定义了RESTRICT
类型的约束,并且存在与多个父行相关联的子行,InnoDB
不会允许删除任何父行。以下示例说明了这一点mysql> CREATE TABLE parent ( -> id INT, -> INDEX (id) -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.04 sec) mysql> CREATE TABLE child ( -> id INT, -> parent_id INT, -> INDEX par_ind (parent_id), -> FOREIGN KEY (parent_id) -> REFERENCES parent(id) -> ON DELETE RESTRICT -> ) ENGINE=InnoDB; Query OK, 0 rows affected (0.02 sec) mysql> INSERT INTO parent (id) -> VALUES ROW(1), ROW(2), ROW(3), ROW(1); Query OK, 4 rows affected (0.01 sec) Records: 4 Duplicates: 0 Warnings: 0 mysql> INSERT INTO child (id,parent_id) -> VALUES ROW(1,1), ROW(2,2), ROW(3,3); Query OK, 3 rows affected (0.01 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> DELETE FROM parent WHERE id=1; ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERENCES `parent` (`id`) ON DELETE RESTRICT)
如果
ON UPDATE CASCADE
或ON UPDATE SET NULL
在同一级联更新期间递归更新它之前已更新的 同一表,它将表现得像RESTRICT
一样。这意味着您不能使用自引用ON UPDATE CASCADE
或ON UPDATE SET NULL
操作。这是为了防止由级联更新引起的无限循环。另一方面,自引用ON DELETE SET NULL
是可能的,自引用ON DELETE CASCADE
也是可能的。级联操作的嵌套深度不能超过 15 级。在插入、删除或更新多行的 SQL 语句中,外键约束(如唯一约束)逐行进行检查。在执行外键检查时,
InnoDB
会在必须检查的子记录或父记录上设置共享行级锁。MySQL 会立即检查外键约束;检查不会延迟到事务提交。根据 SQL 标准,默认行为应该是延迟检查。也就是说,约束只在处理完 整个 SQL 语句 后才进行检查。这意味着无法使用外键删除引用自身的行。包括
InnoDB
在内的所有存储引擎都不识别或强制执行引用完整性约束定义中使用的MATCH
子句。使用显式的MATCH
子句不会产生指定的效果,并且会导致ON DELETE
和ON UPDATE
子句被忽略。应避免指定MATCH
。SQL 标准中的
MATCH
子句控制如何处理复合(多列)外键中与引用表中的主键进行比较时的NULL
值。MySQL 本质上实现了由MATCH SIMPLE
定义的语义,它允许外键完全或部分为NULL
。在这种情况下,即使包含此类外键的(子表)行与引用(父)表中的任何行都不匹配,也可以插入该行。(可以使用触发器来实现其他语义。)引用非
UNIQUE
键的FOREIGN KEY
约束不是标准 SQL,而是InnoDB
的扩展,现在已被弃用,必须通过设置restrict_fk_on_non_standard_key
来启用。您应该预期在 MySQL 的未来版本中会删除对非标准键使用的支持,现在就迁移到其他方法。根据 SQL 标准,
NDB
存储引擎要求对任何引用为外键的列设置显式唯一键(或主键)。对于不支持外键的存储引擎(如
MyISAM
),MySQL 服务器会解析并忽略外键规范。MySQL 会解析但忽略 “内联
REFERENCES
规范”(如 SQL 标准中定义的),其中引用被定义为列规范的一部分。MySQL 仅在作为单独的FOREIGN KEY
规范的一部分指定时才会接受REFERENCES
子句。将列定义为使用
REFERENCES
子句不会产生实际效果,并且 仅用作对您当前定义的列打算引用另一个表中的列的备忘或注释。在使用此语法时,重要的是要意识到tbl_name
(col_name
)MySQL 不会执行任何检查以确保
col_name
确实存在于tbl_name
中(甚至tbl_name
本身是否存在)。MySQL 不会对
tbl_name
执行任何操作,例如响应对您正在定义的表中的行执行的操作而删除行;换句话说,此语法不会引发任何ON DELETE
或ON UPDATE
行为。(虽然您可以在REFERENCES
子句中编写ON DELETE
或ON UPDATE
子句,但它也会被忽略。)此语法创建了一个 列;它 不会 创建任何类型的索引或键。
您可以将这样创建的列用作连接列,如下所示
CREATE TABLE person ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, name CHAR(60) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE shirt ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, style ENUM('t-shirt', 'polo', 'dress') NOT NULL, color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL, owner SMALLINT UNSIGNED NOT NULL REFERENCES person(id), PRIMARY KEY (id) ); INSERT INTO person VALUES (NULL, 'Antonio Paz'); SELECT @last := LAST_INSERT_ID(); INSERT INTO shirt VALUES ROW(NULL, 'polo', 'blue', @last), ROW(NULL, 'dress', 'white', @last), ROW(NULL, 't-shirt', 'blue', @last); INSERT INTO person VALUES (NULL, 'Lilliana Angelovska'); SELECT @last := LAST_INSERT_ID(); INSERT INTO shirt VALUES ROW(NULL, 'dress', 'orange', @last), ROW(NULL, 'polo', 'red', @last), ROW(NULL, 'dress', 'blue', @last), ROW(NULL, 't-shirt', 'white', @last); SELECT * FROM person; +----+---------------------+ | id | name | +----+---------------------+ | 1 | Antonio Paz | | 2 | Lilliana Angelovska | +----+---------------------+ SELECT * FROM shirt; +----+---------+--------+-------+ | id | style | color | owner | +----+---------+--------+-------+ | 1 | polo | blue | 1 | | 2 | dress | white | 1 | | 3 | t-shirt | blue | 1 | | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | | 7 | t-shirt | white | 2 | +----+---------+--------+-------+ SELECT s.* FROM person p INNER JOIN shirt s ON s.owner = p.id WHERE p.name LIKE 'Lilliana%' AND s.color <> 'white'; +----+-------+--------+-------+ | id | style | color | owner | +----+-------+--------+-------+ | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | +----+-------+--------+-------+
当以这种方式使用时,
REFERENCES
子句不会显示在SHOW CREATE TABLE
或DESCRIBE
的输出中。mysql> SHOW CREATE TABLE shirt\G *************************** 1. row *************************** Table: shirt Create Table: CREATE TABLE `shirt` ( `id` smallint(5) unsigned NOT NULL auto_increment, `style` enum('t-shirt','polo','dress') NOT NULL, `color` enum('red','blue','orange','white','black') NOT NULL, `owner` smallint(5) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
有关外键约束的信息,请参见 第 15.1.20.5 节,“外键约束”。