为什么 MySQL 没有正确命名我的外键?

Why is MySQL not properly naming my foreign key?

请考虑以下 SQL 我 运行 在 MySQL 8.0.22 中(在 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 时,为什么 KEY 的名称为 fk_pet_person?我怎样才能让它这样命名?

您混淆了 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

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 参考手册第 13.1.20.5 FOREIGN KEY Constraints:

的这段中可以明显看出两者

Prior to MySQL 8.0.16, if the CONSTRAINT symbol clause was not defined, or a symbol was not included following the CONSTRAINT keyword, both InnoDB and NDB storage engines would use the FOREIGN_KEY index_name if defined. In MySQL 8.0.16 and higher, the FOREIGN_KEY index_name is ignored.

第一个原因是我运行代码在MySQL8.0.22.

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

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