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


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.