【问题标题】:Why is MySQL not properly naming my foreign key?为什么 MySQL 没有正确命名我的外键?
【发布时间】:2021-04-05 10:28:58
【问题描述】:

请考虑以下我在 MySQL 8.0.22 中运行的 SQL 代码(在 InnoDB 数据库中):

CREATE TABLE `person` (
  `person_id` smallint unsigned NOT NULL AUTO_INCREMENT, 
  `name` varchar(128) NOT NULL,
  PRIMARY KEY (`person_id`)
);

CREATE TABLE `pet` (
  `pet_id` smallint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  PRIMARY KEY (`pet_id`)
);

ALTER TABLE `pet`
  ADD COLUMN `owner_id` smallint unsigned;

ALTER TABLE `pet`
  ADD CONSTRAINT `fk_pet_person`
  FOREIGN KEY `idx_fk_pet_person` (`owner_id`)
  REFERENCES `person` (`person_id`);

SHOW CREATE TABLE pet;

SHOW CREATE TABLE pet 的输出是:

CREATE TABLE `pet` (
  `pet_id` smallint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `owner_id` smallint unsigned DEFAULT NULL,
  PRIMARY KEY (`pet_id`),
  KEY `fk_pet_person` (`owner_id`),
  CONSTRAINT `fk_pet_person` FOREIGN KEY (`owner_id`) REFERENCES `person` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

在上面的输出中,当我在ALTER TABLE 命令中将其名称指定为idx_fk_pet_person 时,为什么键名为fk_pet_person?我怎样才能让它如此命名?

【问题讨论】:

    标签: mysql sql foreign-keys create-table


    【解决方案1】:

    您混淆了 FOREIGN KEY 和使其工作的 INDEX。请注意 - 代码中显示的约束定义中没有表达式名称(看起来像索引定义但不是)。

    当没有合适的索引让约束起作用时,则自动创建该索引,并将约束名称用作索引名称。如果存在合适的索引,则不会发生自动创建。

    如果您希望索引具有定义的名称,那么您必须在创建约束之前在单独的ALTER TABLE(子)语句中创建此索引:

    ALTER TABLE `pet`
      ADD KEY `idx_fk_pet_person` (`owner_id`),
      ADD CONSTRAINT `fk_pet_person`
      FOREIGN KEY (`owner_id`)
      REFERENCES `person` (`person_id`);
    

    ALTER TABLE `pet`
      ADD KEY `idx_fk_pet_person` (`owner_id`);
    
    ALTER TABLE `pet`
      ADD CONSTRAINT `fk_pet_person`
      FOREIGN KEY (`owner_id`)
      REFERENCES `person` (`person_id`);
    

    DEMO

    【讨论】:

    • 谢谢。你的解决方法会奏效。然而,我并没有混淆 FK 和约束。我的回答解释了我的代码未按预期设置索引名称的两个原因。
    • @urig 不过,我并没有混淆 FK 和约束。 我已经讲述了 FK 和索引。与 FK 和约束无关 - FK 是约束类型之一。约束是指令/规则(它没有物理实现),而索引是静态存储结构及其数据。
    • 我从来没有说过你在混淆任何东西。
    • @urig 另见dbfiddle.uk/…
    【解决方案2】:

    MySQL 文档指定了创建外键的语法:

    [CONSTRAINT [symbol]] FOREIGN KEY
        [index_name] (col_name, ...)
        REFERENCES tbl_name (col_name,...)
        [ON DELETE reference_option]
        [ON UPDATE reference_option]
    
    reference_option:
        RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
    

    我的代码没有设置[index_name] 而是自动将索引名称设置为与约束名称相同的原因有两个。从 MySQL 8.0 Reference Manual, section 13.1.20.5 FOREIGN KEY Constraints 的这一段中可以看出这两者:

    在 MySQL 8.0.16 之前,如果未定义 CONSTRAINT 符号子句,或者未在 CONSTRAINT 关键字后面包含符号,则 InnoDB 和 NDB 存储引擎都将使用 FOREIGN_KEY 索引名称(如果已定义)。在 MySQL 8.0.16 及更高版本中, FOREIGN_KEY index_name 被忽略。

    第一个原因是我在 MySQL 8.0.22 中运行代码。

    第二个原因是我应该省略约束名称。

    @akina 的答案中的解决方法确实使我能够为与约束名称不同的外键(索引)指定一个名称。

    【讨论】:

    • 您的代码在 8.0 和 5.6 中提供相同的输出/dbfiddle.uk/…
    • @akina 你是对的。那是因为我在代码中指定了约束名称。只有当它被省略时,MySQL 才会使用指定的索引名称作为索引。
    猜你喜欢
    • 2014-10-19
    • 2020-06-17
    • 1970-01-01
    • 2021-02-05
    • 2022-08-18
    • 2021-07-13
    • 2013-08-19
    • 2015-08-30
    • 2017-10-30
    相关资源
    最近更新 更多