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 约束”.