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;
theJava
quelle
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.

Michel Feldheim
quelle
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.

Dementic
quelle
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.

RonaldBarzell
quelle
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