Die in einer Fremdschlüsseleinschränkung verwendete Spalte kann nicht geändert werden


110

Ich habe diesen Fehler erhalten, als ich versucht habe, meine Tabelle zu ändern.

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

Hier ist meine CREATE TABLE STATEMENT, die erfolgreich ausgeführt wurde.

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

Dann habe ich versucht, diese Anweisung auszuführen und ich habe den obigen Fehler erhalten.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

4
Das obige Beispiel stammt aus dem Buch "Learning SQL, 2nd Edition". Ich hoffe, der Autor Alan Beaulieu nimmt Korrekturen vor.
Dmitry

Antworten:


126

Der Typ und die Definition des Fremdschlüsselfelds und der Referenz müssen gleich sein. Dies bedeutet, dass Ihr Fremdschlüssel das Ändern des Feldtyps nicht zulässt.

Eine Lösung wäre folgende:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

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

Jetzt können Sie Ihre person_id ändern

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

Fremdschlüssel neu erstellen

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

UNLOCK TABLES;

BEARBEITEN: Dank Kommentaren wurden oben Sperren hinzugefügt

Währenddessen müssen Sie das Schreiben in die Datenbank nicht zulassen, da sonst Probleme mit der Datenintegrität auftreten können.

Ich habe oben eine Schreibsperre hinzugefügt

Alle Schreibanfragen in einer anderen Sitzung als Ihrer eigenen ( INSERT, UPDATE, DELETE) warten bis zum Timeout oder UNLOCK TABLES; ausgeführt wird

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

EDIT 2: OP bat um eine detailliertere Erläuterung der Zeile "Der Typ und die Definition des Fremdschlüsselfelds und der Referenz müssen gleich sein. Dies bedeutet, dass Ihr Fremdschlüssel das Ändern des Feldtyps nicht zulässt."

Aus MySQL 5.5 Referenzhandbuch: FOREIGN KEY Constraints

Entsprechende Spalten im Fremdschlüssel und im referenzierten Schlüssel müssen ähnliche interne Datentypen in InnoDB aufweisen, damit sie ohne Typkonvertierung verglichen werden können. Die Größe und das Vorzeichen von Ganzzahltypen müssen identisch sein. Die Länge der Zeichenfolgentypen muss nicht gleich sein. Bei nicht-binären (Zeichen-) Zeichenfolgenspalten müssen Zeichensatz und Sortierung identisch sein.


1
Vergessen Sie nicht, dafür eine Transaktion zu verwenden. Andernfalls wird Ihre Datenbank möglicherweise beschädigt.
Francois Bourgeois

5
Guter Punkt, leider unterstützt MySQL keine Transaktionen rund um DDL-Anweisungen. Offene Transaktionen werden festgeschrieben, bevor eine DDL-Abfrage ausgeführt wird. Siehe dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim

2
Die richtige Anweisung zum Neuerstellen eines Fremdschlüssels wäre: ALTER TALE favor_food ADD CONTRAINT fk_fav_food_person_id AUSLÄNDISCHER SCHLÜSSEL (person_id) REFERENZEN person (id);
Felizardo

1
Warum ändern Sie person_iddirekt nach dem Löschen des Fremdschlüssels? Sieht so aus, als hättest du nichts geändert, da es bereits eine ist SMALLINT UNSIGNED.
Dennis Subachev

1
Wir wissen nicht, was es war, da er nur die Struktur der Referenzierungstabelle gepostet hat. Innodb hat int als internen Typ, smallint usw. sind nur Abkürzungen
Michel Feldheim

197

Sie können Fremdschlüsselprüfungen deaktivieren:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Bitte stellen Sie sicher, dass Sie dies NICHT in der Produktion verwenden und ein Backup haben.


1
Scheint eine sehr unsichere Lösung zu sein. Kann dies möglicherweise zu einem Verlust der Datenintegrität führen?
7.

@Synaps - ja, es könnte sein, wenn Sie löschen / aktualisieren / einfügen. Datenverlust tritt nicht auf, wenn Sie nur eine Tabelle ändern oder Ihre Datenbank
festlegen

2
Diese Lösung ist großartig und kann in der Produktion verwendet werden, wenn Sie Schreibsperren hinzufügen, bevor Sie Ihre Daten ändern, und dann entsperren, wenn Sie fertig sind. Noch besser wäre es, eine SQL-Datei zu verwenden, um Ihre Änderungen in kürzester Zeit vorzunehmen.
Francisco Zarabozo

Gute Lösung für schnelle
Optimierungen

1
Sie benötigen keine Sperre, da dies SET FOREIGN_KEY_CHECKSfür die Sitzung gilt (für andere Sitzungen wird weiterhin die FK-Einschränkung angewendet). Es ist perfekt zum Hinzufügen / Entfernen AUTO_INCREMENT(wodurch der tatsächliche Spaltendatentyp nicht geändert wird), funktioniert jedoch nicht, wenn Sie versuchen, den Spaltendatentyp für "real" (z. B. von SMALLINT in INT) zu ändern, da Sie einen legitimen 150 FK constraint incorrectly formedZeitpunkt erhalten mysql versucht, die alte Tabelle durch die neue zu ersetzen. Verwenden Sie in diesem Fall die akzeptierte Antwort.
Xenos

-3

Wenn Sie Schlüssel (primäre oder fremde) festlegen, legen Sie Einschränkungen für deren Verwendung fest, was wiederum die Möglichkeiten einschränkt, die Sie damit haben. Wenn Sie die Spalte wirklich ändern möchten, können Sie die Tabelle ohne Einschränkungen neu erstellen, obwohl ich dagegen empfehlen würde. Wenn Sie eine Situation haben, in der Sie etwas tun möchten, die jedoch durch eine Einschränkung blockiert ist, wird dies im Allgemeinen am besten gelöst, indem Sie ändern, was Sie tun möchten, anstatt die Einschränkung.


12
Dies ist so eine nicht hilfreiche, nutzlose Antwort!
Ajmedway

3
@ajmedway Dann können Sie eine hilfreiche Antwort schreiben, ohne die anderen Benutzer zu beschuldigen
ich bin die dümmste Person

2
@IamtheMostStupidPerson Es ist viel besser als nur ohne Kommentar abzustimmen. Zumindest kann der Kommentator erraten, warum die Abstimmungen. Solche generischen Antworten wie "Besser auf Nummer sicher gehen" sind nicht sinnvoll.
Csaba Toth
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.