Nie można zmienić kolumny używanej w ograniczeniu klucza obcego

111

Wystąpił ten błąd, gdy próbowałem zmienić mój stół.

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

Oto moje STWÓRZ STANOWISKO TABELI, które zostały pomyślnie uruchomione.

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)
);

Następnie próbowałem wykonać tę instrukcję i otrzymałem powyższy błąd.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
źródło
4
Powyższy przykład pochodzi z książki „Learning SQL, 2nd edition”. Mam nadzieję, że autor Alan Beaulieu wprowadza poprawki.
Dmitry

Odpowiedzi:

126

Typ i definicja pola klucza obcego i odwołania muszą być równe. Oznacza to, że klucz obcy nie pozwala na zmianę typu pola.

Jednym rozwiązaniem byłoby to:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

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

Teraz możesz zmienić swój identyfikator person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

odtworzyć klucz obcy

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

UNLOCK TABLES;

EDYCJA: Dodano blokady powyżej, dzięki komentarzom

Musisz zabronić zapisywania do bazy danych podczas tej czynności, w przeciwnym razie ryzykujesz problemy z integralnością danych.

Dodałem powyżej blokadę zapisu

Wszystkie zapytania zapisujące w dowolnej sesji innej niż Twoja ( INSERT, UPDATE, DELETE) będą czekać do przekroczenia limitu czasu lub UNLOCK TABLES; jest wykonywany

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

EDYCJA 2: OP poprosił o bardziej szczegółowe wyjaśnienie wiersza „Typ i definicja pola klucza obcego oraz odwołanie muszą być równe. Oznacza to, że klucz obcy nie pozwala na zmianę typu pola”.

Z Podręcznika MySQL 5.5: Ograniczenia klucza obcego

Odpowiednie kolumny w kluczu obcym i przywoływanym kluczu muszą mieć podobne wewnętrzne typy danych w InnoDB, aby można je było porównać bez konwersji typu. Rozmiar i znak typów całkowitych muszą być takie same. Długość typów ciągów nie musi być taka sama. W przypadku kolumn z ciągami niebinarnymi (znakowymi) zestaw znaków i sortowanie muszą być takie same.

Michel Feldheim
źródło
1
Nie zapomnij użyć do tego transakcji. W przeciwnym razie baza danych może zostać uszkodzona.
Francois Bourgeois
5
Dobra uwaga, niestety MySQL nie obsługuje transakcji wokół instrukcji DDL. Otwarte transakcje są zatwierdzane przed wykonaniem zapytania DDL, patrz dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim
2
Prawidłowa instrukcja do odtworzenia klucza obcego brzmiałaby: ALTER TALE favourite_food DODAJ KONTRAINT fk_fav_food_person_id KLUCZ OBCY (person_id) REFERENCJE osoba (id);
Felizardo
1
Dlaczego zmodyfikować person_idprawo po rzuceniu klucz obcy? Wygląda na to, że nic nie zmieniłeś, ponieważ jest to już plik SMALLINT UNSIGNED.
Dennis Subachev
1
Nie wiemy, co to było, ponieważ opublikował tylko strukturę tabeli odwołań. Innodb ma int jako typ wewnętrzny, smallint itp. To tylko skróty
Michel Feldheim
198

Możesz wyłączyć sprawdzanie kluczy obcych:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Upewnij się, że NIE używasz tego na produkcji i masz kopię zapasową.

Demencyjny
źródło
1
Wydaje się, że jest to bardzo niepewne rozwiązanie. Czy może to potencjalnie prowadzić do utraty integralności danych?
hrust
@Synaps - tak, może, jeśli usuwasz / aktualizujesz / wstawiasz. utrata danych nie nastąpi, jeśli tylko modyfikujesz tabelę lub umieszczasz bazę danych, z drugiej strony powinieneś ręcznie zweryfikować swoje dane (ponieważ usunąłeś ograniczenia)
Dementic
2
To rozwiązanie jest świetne i możesz go używać w środowisku produkcyjnym, jeśli przed modyfikacją danych zapiszesz blokady, a po zakończeniu odblokujesz. Użycie pliku sql do wprowadzenia zmian w jak najkrótszym czasie byłoby jeszcze lepsze.
Francisco Zarabozo,
Dobre rozwiązanie na szybkie poprawki
Genaut
1
Nie potrzebujesz blokady, która SET FOREIGN_KEY_CHECKSjest objęta zakresem sesji (inne sesje będą nadal miały zastosowane ograniczenie FK). Jest idealny do dodawania / usuwania AUTO_INCREMENT(co nie zmienia rzeczywistego typu danych kolumny), ale nie zadziała, jeśli spróbujesz zmienić typ danych kolumny na „prawdziwy” (powiedzmy, z SMALLINT na INT), ponieważ uzyskasz legalny, 150 FK constraint incorrectly formedkiedy mysql próbuje zastąpić starą tabelę nową. W takim przypadku użyj zaakceptowanej odpowiedzi.
Xenos
-3

Kiedy ustawiasz klucze (podstawowe lub obce), ustawiasz ograniczenia dotyczące tego, jak mogą być używane, co z kolei ogranicza to, co możesz z nimi zrobić. Jeśli naprawdę chcesz zmienić kolumnę, możesz ponownie utworzyć tabelę bez ograniczeń, chociaż odradzam to. Ogólnie rzecz biorąc, jeśli masz sytuację, w której chcesz coś zrobić, ale jest to blokowane przez ograniczenie, najlepiej rozwiązać to, zmieniając to, co chcesz zrobić, zamiast ograniczenia.

RonaldBarzell
źródło
12
To jest TAKIE nieprzydatna, bezużyteczna odpowiedź!
ajmedway
3
@ajmedway Następnie możesz napisać pomocną odpowiedź, nie obwiniając innych użytkowników
Jestem najbardziej głupią osobą
2
@IamtheMostStupidPerson To znacznie lepsze niż zwykłe głosowanie w dół bez komentarza. Przynajmniej komentator może zgadnąć, dlaczego głosy przeciwne. Takie ogólne odpowiedzi, jak „lepiej być bezpiecznym niż przepraszać”, nie są przydatne.
Csaba Toth