MySQL 实现外键约束与 SQL 标准的差异在以下几个关键方面:
-
如果父表中有多行具有相同的引用键值,
InnoDB
将执行外键检查,如同其他父行具有相同键值不存在一样。例如,如果您定义了RESTRICT
类型的约束,并且有一个子行具有多个父行,InnoDB
不允许删除任何父行。 -
如果
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
。在这种情况下,即使子表中的行不匹配参照表中的任何行,也可以插入该行。(可以使用触发器实现其他语义。) -
MySQL 需要参照列被索引以提高性能。但是,MySQL 不强制参照列为
UNIQUE
或声明为NOT NULL
。一个
FOREIGN KEY
约束引用非UNIQUE
键不是标准 SQL,而是InnoDB
扩展。另一方面,NDB
存储引擎需要在任何作为外键引用的列上显式定义唯一键(或主键)。处理外键引用非唯一键或包含
NULL
值的键的操作,如UPDATE
或DELETE CASCADE
,其行为未定义。你被建议使用仅引用唯一(包括PRIMARY
)和NOT NULL
键的外键。 -
对于不支持外键的存储引擎(如
MyISAM
),MySQL 服务器解析并忽略外键规范。 -
MySQL 解析但忽略 “内联
REFERENCES
规范”(如 SQL 标准所定义),其中引用是作为列规范的一部分定义的。MySQL 只接受REFERENCES
子句作为单独的FOREIGN KEY
规范的一部分。定义一个列使用
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 (NULL, 'polo', 'blue', @last), (NULL, 'dress', 'white', @last), (NULL, 't-shirt', 'blue', @last); INSERT INTO person VALUES (NULL, 'Lilliana Angelovska'); SELECT @last := LAST_INSERT_ID(); INSERT INTO shirt VALUES (NULL, 'dress', 'orange', @last), (NULL, 'polo', 'red', @last), (NULL, 'dress', 'blue', @last), (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
的输出中显示: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 节,“FOREIGN KEY 约束”。