【发布时间】:2019-05-14 13:05:21
【问题描述】:
在删除和添加外键时,ALTER TABLE 语句的行为方式似乎不一致。有时关联的索引会被重命名,有时则不会。我已经确定了发生这种情况的情况:
方法 #1
一个简单的person 表,具有一个自动递增的主键id 和一个自身的外键列self_id。注意:两个单独的表的行为是相同的,我使用了一个表来简化示例。
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`self_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `self_id_fk` (`self_id`),
CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
接下来我通过删除现有的外键然后添加一个新的来重命名外键。这是在单个语句中完成的,但如果拆分为多个 ALTER TABLE 语句,行为是相同的。
ALTER TABLE `person`
DROP FOREIGN KEY `self_id_fk`,
ADD CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);
此语句后的表格如下:
CREATE TABLE `person` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`self_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `self_id_fk` (`self_id`),
CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
请注意,外键已重命名,但索引没有。
方法 #2
另一种方法是首先创建没有任何外键或索引的表:
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`self_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
接下来添加外键约束:
ALTER TABLE `person` ADD CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);
结果如下表:
CREATE TABLE `person` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`self_id` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `self_id_fk` (`self_id`),
CONSTRAINT `self_id_fk` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
请注意,由于行为described in the documentation,会自动创建索引
... 如果引用表自动创建索引 不存在
接下来我以与APPROACH #1相同的方式重命名外键:
ALTER TABLE `person`
DROP FOREIGN KEY `self_id_fk`,
ADD CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`);
但是这次外键和索引都被重命名了:
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`self_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a_new_fk_name` (`self_id`),
CONSTRAINT `a_new_fk_name` FOREIGN KEY (`self_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
对发生的事情有任何解释吗?就好像 MySQL 正在跟踪哪些索引是“自动创建的”,然后在外键更改时重命名它们。在运行 ALTER TABLE 语句之前,两种方法的表 DDL 是相同的,因此 MySQL 必须跟踪一些“内部引擎状态”。
仅查看 DDL 无法预测运行 ALTER TABLE 语句时 MySQL 的行为方式。这意味着一旦一个简单的ALTER TABLE 语句运行,两个“架构相同”的数据库最终可能会出现架构不匹配的情况。
【问题讨论】:
-
如果你使用
ADD FOREIGN KEY让 MySQL 生成外键名称(例如table_ibfk_1),事情会变得更加混乱 - 稍后添加命名外键似乎会导致隐式索引被重命名匹配新的外键,即使之前的外键仍然存在!
标签: mysql sql indexing foreign-keys innodb