Невозможно изменить столбец, используемый в ограничении внешнего ключа

111

Я получил эту ошибку, когда пытался изменить свою таблицу.

Error Code: 1833. Cannot change column 'person_id': used in a foreign key constraint 'fk_fav_food_person_id' of table 'table.favorite_food'

Вот мое ЗАЯВЛЕНИЕ СОЗДАТЬ ТАБЛИЦУ, которое прошло успешно.

CREATE TABLE favorite_food(
    person_id SMALLINT UNSIGNED,
    food VARCHAR(20),
    CONSTRAINT pk_favorite_food PRIMARY KEY(person_id,food),
    CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
    REFERENCES person (person_id)
);

Затем я попытался выполнить этот оператор и получил указанную выше ошибку.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
источник
4
Приведенный выше пример взят из книги «Изучение SQL, 2-е издание». Я надеюсь, что автор, Алан Болье, внесет исправления.
Дмитрий

Ответы:

126

Тип и определение поля внешнего ключа и ссылки должны быть одинаковыми. Это означает, что ваш внешний ключ запрещает изменение типа вашего поля.

Одно из решений было бы таким:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

ALTER TABLE favorite_food
    DROP FOREIGN KEY fk_fav_food_person_id,
    MODIFY person_id SMALLINT UNSIGNED;

Теперь вы можете изменить свой person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

воссоздать внешний ключ

ALTER TABLE favorite_food
    ADD CONSTRAINT fk_fav_food_person_id FOREIGN KEY (person_id)
          REFERENCES person (person_id);

UNLOCK TABLES;

РЕДАКТИРОВАТЬ: добавлены блокировки выше, благодаря комментариям

Вы должны запретить запись в базу данных при этом, иначе вы рискуете проблемами целостности данных.

Я добавил блокировку записи выше

Все запросы на запись в любом другом сеансе, кроме вашего ( INSERT, UPDATE, DELETE), будут ждать до тайм-аута или UNLOCK TABLES; выполняется

http://dev.mysql.com/doc/refman/5.5/en/lock-tables.html

РЕДАКТИРОВАТЬ 2: OP запросил более подробное объяснение строки «Тип и определение поля внешнего ключа и ссылки должны быть одинаковыми. Это означает, что ваш внешний ключ запрещает изменение типа вашего поля».

Из Справочного руководства MySQL 5.5: Ограничения FOREIGN KEY

Соответствующие столбцы во внешнем ключе и указанном ключе должны иметь похожие внутренние типы данных внутри InnoDB, чтобы их можно было сравнивать без преобразования типа. Размер и знак целочисленных типов должны быть одинаковыми. Длина строковых типов не обязательно должна быть одинаковой. Для столбцов недвоичной (символьной) строки набор символов и сопоставление должны быть одинаковыми.

Мишель Фельдхейм
источник
1
Не забудьте использовать для этого транзакцию. В противном случае ваша база данных может быть повреждена.
Francois Bourgeois
5
Хороший момент, к сожалению, MySQL не поддерживает транзакции вокруг операторов DDL. Открытые транзакции совершаются до выполнения DDL-запроса, см. Dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Мишель Фельдхейм,
2
Правильный оператор для воссоздания внешнего ключа: ALTER TALE favourite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCES person (id);
Felizardo
1
Почему вы меняете person_idсразу после удаления внешнего ключа? Похоже, вы ничего не меняли, так как это уже файл SMALLINT UNSIGNED.
Денис Субачев
1
Мы не знаем, что это было, поскольку он опубликовал только структуру таблицы ссылок. Innodb имеет int как внутренний тип, smallint и т. Д. - только ярлыки
Мишель Фельдхейм,
198

Вы можете отключить проверку внешнего ключа:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Пожалуйста, убедитесь, что вы НЕ используете это в производственной среде и имеете резервную копию.

Дементный
источник
1
Вроде бы очень небезопасное решение. Может ли это привести к потере целостности данных?
hrust 07
@Synaps - да, может, если вы удаляете / обновляете / вставляете. потеря данных не произойдет, если вы просто изменяете таблицу или заполняете свой db, с другой стороны, вам следует вручную проверять свои данные (поскольку вы удаляете ограничения)
Dementic
2
Это отличное решение, и вы можете использовать его в производственной среде, если вы объявите блокировку записи перед изменением данных, а затем разблокируете его, когда закончите. Еще лучше было бы использовать файл sql для внесения изменений в кратчайшие сроки.
Франсиско Зарабозо,
Хорошее решение для быстрых настроек
Genaut
1
Вам не нужна блокировка, как SET FOREIGN_KEY_CHECKSв сеансе (другие сеансы по-прежнему будут иметь ограничение FK). Он идеально подходит для добавления / удаления AUTO_INCREMENT(который не меняет фактический тип данных столбца), но он не будет работать, если вы попытаетесь изменить тип данных столбца на «реальный» (скажем, с SMALLINT на INT), так как вы получите законный, 150 FK constraint incorrectly formedкогда mysql пытается заменить старую таблицу новой. В таком случае используйте принятый ответ.
Xenos
-3

Когда вы устанавливаете ключи (первичные или внешние), вы устанавливаете ограничения на их использование, что, в свою очередь, ограничивает то, что вы можете с ними делать. Если вы действительно хотите изменить столбец, вы можете воссоздать таблицу без ограничений, хотя я бы не рекомендовал этого делать. Вообще говоря, если у вас есть ситуация, в которой вы хотите что-то сделать, но это заблокировано ограничением, ее лучше всего разрешить, изменив то, что вы хотите сделать, а не ограничение.

Рональд Барзелл
источник
13
Это ТАКОЙ бесполезный, бесполезный ответ!
ajmedway
3
@ajmedway Тогда вы можете написать полезный ответ, не обвиняя других пользователей
я самый глупый человек
2
@IamtheMostStupidPerson Это намного лучше, чем просто голосование против без комментариев. По крайней мере, комментатор может догадаться, почему голосование против. Такие общие ответы, как «лучше перестраховаться, чем сожалеть», бесполезны.
Csaba Toth