Неможливо змінити стовпчик, що використовується в обмеженні іноземного ключа


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;

EDIT: Додано блокування вище, завдяки коментарям

Під час цього вам потрібно заборонити писати в базу даних, інакше ви ризикуєте проблемами з цілісністю даних.

Я додав блокування запису вище

Усі запити щодо запису в будь-якому іншому сеансі, крім вашого власного ( INSERT, UPDATE, DELETE), будуть чекати до тайм-ауту або UNLOCK TABLES; виконується

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

EDIT 2: ОП попросило більш детальне пояснення рядка "Тип та визначення поля іноземного ключа та посилання повинні бути рівними. Це означає, що ваш зовнішній ключ забороняє змінювати тип вашого поля".

З довідкового посібника MySQL 5.5: ЗНОЗОВНІ КЛЮЧІ

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


1
Не забудьте використати для цього транзакцію. Інакше ваша база може пошкодитися.
Франсуа Буржуа

5
Добре, на жаль, MySQL не підтримує транзакції навколо операторів DDL. Відкриті транзакції здійснюються перед виконанням запиту DDL, див. Dev.mysql.com/doc/refman/5.5/uk/implicit-commit.html
Michel Feldheim

2
Правильним твердженням для відтворення іноземного ключа було б: АЛЬТЕР ТЕЙЛЬ улюблений_фуд ДОДАТИ КОНТРАКТ fk_fav_food_person_id ІНФОРМАЦІЙНИЙ КЛЮЧ (person_id) ПОСЛУГИ особа (id);
Фелізардо

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
Здається, це дуже небезпечне рішення. Чи може це потенційно призвести до втрати цілісності даних?
ранок

@Synaps - так, це може бути, якщо ви видаляєте / оновлюєте / вставляєте. втрата даних не відбудеться, якщо ви просто змінюєте таблицю або висіваєте свій db, з іншого боку, ви повинні вручну перевірити свої дані (оскільки ви
усунете

2
Це рішення чудово, і ви можете використовувати його у виробництві, якщо рекламувати блокування записів перед зміною даних, а потім розблокувати, коли закінчите. Використання файлу sql для внесення змін за найкоротший проміжок часу було б ще краще.
Франциско Зарабобозо

Гарне рішення для швидких налаштувань
Genaut

1
Блокування не потрібно, як SET FOREIGN_KEY_CHECKSпередбачено сеансом (на інших сесіях все ще буде застосовано обмеження FK). Він ідеально підходить для додавання / видалення AUTO_INCREMENT(що не змінює фактичний тип стовпця даних), але це не спрацює, якщо ви спробуєте змінити тип даних стовпця на "реальний" (скажімо, від SMALLINT до INT), оскільки ви отримаєте законний, 150 FK constraint incorrectly formedколи mysql намагається замінити стару таблицю новою. У такому випадку використовуйте прийняту відповідь.
Ксенос

-3

Під час встановлення ключів (первинних чи іноземних) ви встановлюєте обмеження щодо їх використання, що, в свою чергу, обмежує те, що ви можете зробити з ними. Якщо ви дійсно хочете змінити стовпчик, ви можете заново створити таблицю без обмежень, хоча я б рекомендував проти неї. Взагалі кажучи, якщо у вас є ситуація, в якій ви хочете щось зробити, але це блокується обмеженням, це краще вирішити, змінивши те, що ви хочете зробити, а не обмеження.


12
Це ТАКОЖ негідна, марна відповідь!
ajmedway

3
@ajmedway Тоді ви можете написати корисну відповідь, не звинувачуючи інших користувачів
я

2
@IamtheMostStupidPerson Це набагато краще, ніж просто зволікати без коментарів. Принаймні, коментатор може здогадатися, навіщо знищується. Такі загальні відповіді, як "краще бути безпечним, ніж шкодувати", не корисні.
Csaba Toth
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.