1.7.2.3 外键约束差异
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标准,缺省行为应该是延迟检查。这意味着不能使用外键删除指向自己的一行记录。 -
包括
InnoDB在内的所有存储引擎都不会识别或强制执行referential-integrity约束定义中的MATCH子句。使用明确的MATCH子句不起预期效果,并且会忽略ON DELETE和ON UPDATE子句。指定MATCH应该被避免。SQL标准中的
MATCH子句控制多列外键中NULL值在与参考表的主键比较时的处理方式。MySQL实际实现了MATCH SIMPLE语义,允许外键完全或部分为NULL。因此,即使该行不匹配任何记录,也可以插入子表(child table)。(使用触发器也可以实现其他语义。) -
MySQL 需要引用列被索引,以提高性能。但是,MySQL 不强制要求引用列为
UNIQUE或声明为NOT NULLFOREIGN KEY约束引用非唯一键不是标准SQL,而是InnoDB的扩展。反之,NDB存储引擎则要求对任何被引用为外键的列必须有明确的唯一键(或主键)。非唯一键或包含
NULL值的外键引用在操作,如UPDATE或DELETE CASCADE,其处理方式不明确。因此,建议使用只引用唯一(包括主键)和NOT NULL键的外键。 -
对于不支持外键的存储引擎(如
MyISAM),MySQL 服务器将忽略外键约束。 -
MySQL 解析但忽略了“inline
REFERENCESspecifications”(按照 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节,“外键约束”。