Кажется, я столкнулся с несоответствием поведения оператора 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;
Обратите внимание, что индекс создается автоматически из-за поведения , описанного в документации
... для ссылочной таблицы автоматически создается индекс, если он
не существует
Далее я переименую внешний ключ так же, как 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, невозможно предсказать, как MySQL будет вести себя при выполнении оператора ALTER TABLE
. Это означает, что две «идентичные схеме» базы данных могут получить несоответствующую схему после выполнения простого оператора ALTER TABLE
.