在删除和添加外键时,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
... 在引用表上自动创建索引
不存在
接下来,我用与方法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无法预测MySQL在运行
ALTER TABLE
语句时的行为方式。这意味着,一旦运行了一个简单的ALTER TABLE
语句,两个“模式相同”的数据库可能会以不匹配的模式结束。 最佳答案
我也注意到了同样的事情,但我从未见过任何官方文件来解释这一点。
我同意InnoDB“知道”哪些索引是隐式创建的,哪些是显式创建的。但我不知道它在哪里追踪这些信息。InnoDB在INFORMATION_SCHEMA
表中公开了许多元数据,但它必须在内部数据字典中存储更多信息。
这是MySQL 5.7及更早版本中关于内部DD的唯一文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-data-dictionary.html
我唯一的建议是,如果需要索引名是可预测的,则需要显式创建索引,然后创建外键约束。不要依赖外键隐式创建索引。
MySQL 8.0完全重新设计了数据字典,因此您观察到的行为可能会再次发生变化。https://dev.mysql.com/doc/refman/8.0/en/data-dictionary.html