MySQL 9.0 参考手册  /  ...  /  FOREIGN KEY 约束差异

1.7.2.3 FOREIGN KEY 约束差异

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 CASCADEON UPDATE SET NULL 递归更新其先前在同一级联期间更新的同一表,它将像 RESTRICT 一样执行。这意味着您不能使用自引用 ON UPDATE CASCADEON UPDATE SET NULL 操作。这是为了防止级联更新导致的无限循环。另一方面,自引用 ON DELETE SET NULL 是可以的,自引用 ON DELETE CASCADE 也是可以的。级联操作的嵌套深度不能超过 15 级。

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

  • 没有存储引擎(包括 InnoDB)识别或强制执行引用完整性约束定义中使用的 MATCH 子句。使用显式 MATCH 子句不会产生指定的效果,并且会导致忽略 ON DELETEON 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 TABLEDESCRIBE 的输出结果来查看它是否有效,例如

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