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


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;

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.