Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

1.6.2.3 外键约束差异

MySQL 实现外键约束与 SQL 标准的差异在以下几个关键方面:

  • 如果父表中有多行具有相同的引用键值,InnoDB 将执行外键检查,如同其他父行具有相同键值不存在一样。例如,如果您定义了 RESTRICT 类型的约束,并且有一个子行具有多个父行,InnoDB 不允许删除任何父行。

  • 如果 ON UPDATE CASCADEON UPDATE SET NULL 递归更新同一个表,它将像 RESTRICT 一样执行。这意味着您不能使用自引用的 ON UPDATE CASCADEON UPDATE SET NULL 操作。这是为了防止级联更新导致的无限循环。另一方面,自引用的 ON DELETE SET NULLON DELETE CASCADE 是可能的。级联操作不能嵌套超过 15 层。

  • 在插入、删除或更新多行的 SQL 语句中,外键约束(如唯一约束)是逐行检查的。当执行外键检查时,InnoDB 将在子表或父表记录上设置共享行级锁。MySQL 立即检查外键约束,而不是延迟到事务提交。根据 SQL 标准,默认行为应该是延迟检查,即约束只有在整个 SQL 语句处理完成后才检查。这意味着您不能删除一个引用自己的行使用外键。

  • 包括 InnoDB 在内的任何存储引擎都不识别或执行在参照完整性约束定义中使用的 MATCH 子句。使用明确的 MATCH 子句不会产生指定的效果,并且会导致 ON DELETEON UPDATE 子句被忽略。因此,应该避免使用 MATCH 子句。

    SQL 标准中的 MATCH 子句控制在参照表中的主键与复合外键中的 NULL 值的比较方式。MySQL 实际上实现了 MATCH SIMPLE 语义,这允许外键部分或全部为 NULL。在这种情况下,即使子表中的行不匹配参照表中的任何行,也可以插入该行。(可以使用触发器实现其他语义。)

  • MySQL 需要参照列被索引以提高性能。但是,MySQL 不强制参照列为 UNIQUE 或声明为 NOT NULL

    一个 FOREIGN KEY 约束引用非 UNIQUE 键不是标准 SQL,而是 InnoDB 扩展。另一方面,NDB 存储引擎需要在任何作为外键引用的列上显式定义唯一键(或主键)。

    处理外键引用非唯一键或包含 NULL 值的键的操作,如 UPDATEDELETE 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 DELETEON UPDATE 行为。(尽管你可以在 REFERENCES 子句中编写 ON DELETEON 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 TABLEDESCRIBE 的输出中显示:

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