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 版本中会删除对非标准键使用的支持,现在应该迁移到其他方法。NDB存储引擎要求任何作为外键引用的列上都具有显式唯一键(或主键),符合 SQL 标准。对于不支持外键的存储引擎(例如
MyISAM),MySQL 服务器会解析并忽略外键规范。以前版本的 MySQL 会解析但忽略“内联
REFERENCES规范”(如 SQL 标准中定义的那样),其中引用被定义为列规范的一部分。MySQL 9.0 会接受此类REFERENCES子句并强制执行由此创建的外键。此外,MySQL 9.0 允许隐式引用父表的 primary key。这意味着以下语法是有效的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('tee', 'polo', 'dress') NOT NULL, color ENUM('red', 'blue', 'yellow', 'white', 'black') NOT NULL, owner SMALLINT UNSIGNED NOT NULL REFERENCES person, PRIMARY KEY (id) );您可以通过检查
SHOW CREATE TABLE或DESCRIBE的输出结果来查看它是否有效,例如mysql> SHOW CREATE TABLE shirt\G *************************** 1. row *************************** Table: shirt Create Table: CREATE TABLE `shirt` ( `id` smallint unsigned NOT NULL AUTO_INCREMENT, `style` enum('tee','polo','dress') NOT NULL, `color` enum('red','blue','yellow','white','black') NOT NULL, `owner` smallint unsigned NOT NULL, PRIMARY KEY (`id`), KEY `owner` (`owner`), CONSTRAINT `shirt_ibfk_1` FOREIGN KEY (`owner`) REFERENCES `person` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
有关外键约束的更多信息,请参见第 15.1.20.5 节,“FOREIGN KEY 约束”.