Impossible de modifier la colonne utilisée dans une contrainte de clé étrangère

111

J'ai eu cette erreur lorsque j'essayais de modifier ma table.

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

Voici mon CREATE TABLE STATEMENT qui s'est exécuté avec succès.

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

Ensuite, j'ai essayé d'exécuter cette déclaration et j'ai eu l'erreur ci-dessus.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
la source
4
L'exemple ci-dessus est tiré du livre "Learning SQL, 2nd edition". J'espère que l'auteur, Alan Beaulieu apporte des corrections.
Dmitry

Réponses:

126

Le type et la définition du champ de clé étrangère et de la référence doivent être égaux. Cela signifie que votre clé étrangère ne permet pas de modifier le type de votre champ.

Une solution serait la suivante:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

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

Vous pouvez maintenant changer votre person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

recréer la clé étrangère

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

UNLOCK TABLES;

EDIT: Ajout de verrous ci-dessus, grâce aux commentaires

Vous devez interdire l'écriture dans la base de données pendant que vous faites cela, sinon vous risquez des problèmes d'intégrité des données.

J'ai ajouté un verrou d'écriture ci-dessus

Toutes les requêtes d'écriture dans une session autre que la vôtre ( INSERT, UPDATE, DELETE) attendront jusqu'à l'expiration du délai ou UNLOCK TABLES; est exécuté

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

EDIT 2: OP a demandé une explication plus détaillée de la ligne "Le type et la définition du champ de clé étrangère et de la référence doivent être égaux. Cela signifie que votre clé étrangère ne permet pas de changer le type de votre champ."

Depuis le manuel de référence de MySQL 5.5: Contraintes FOREIGN KEY

Les colonnes correspondantes dans la clé étrangère et la clé référencée doivent avoir des types de données internes similaires dans InnoDB afin de pouvoir être comparées sans conversion de type. La taille et le signe des types entiers doivent être identiques. La longueur des types de chaîne n'a pas besoin d'être la même. Pour les colonnes de chaînes non binaires (caractères), le jeu de caractères et le classement doivent être identiques.

Michel Feldheim
la source
1
N'oubliez pas d'utiliser une transaction pour cela. Sinon, votre base de données pourrait être corrompue.
Francois Bourgeois
5
Bon point, malheureusement MySQL ne prend pas en charge les transactions autour des instructions DDL. Les transactions ouvertes sont validées avant qu'une requête DDL ne soit exécutée voir dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim
2
L'instruction correcte pour recréer une clé étrangère serait: ALTER TALE favorite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCES person (id);
Felizardo
1
Pourquoi modifiez-vous person_idjuste après avoir déposé la clé étrangère? On dirait que vous n'avez rien changé car c'est déjà un fichier SMALLINT UNSIGNED.
Dennis Subachev
1
Nous ne savons pas ce que c'était car il n'a publié que la structure de la table de référencement. Innodb a int comme type interne, smallint etc ne sont que des raccourcis
Michel Feldheim
198

Vous pouvez désactiver les vérifications de clé étrangère:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Veuillez vous assurer de NE PAS utiliser ceci en production et d'avoir une sauvegarde.

Démence
la source
1
Semble être une solution très peu sûre. Cela peut-il potentiellement entraîner une perte d'intégrité des données?
hrust
@Synaps - oui cela pourrait si vous supprimez / mettez à jour / insérez. la perte de données ne se produira pas si vous ne faites que modifier une table ou ensemencer votre base de données, par contre vous devez valider manuellement vos données (puisque vous supprimez les contraintes)
Dementic
2
Cette solution est excellente et vous pouvez l'utiliser en production si vous annoncez des verrous d'écriture avant de modifier vos données, puis un déverrouillage lorsque vous avez terminé. Utiliser un fichier sql pour effectuer vos modifications dans les plus brefs délais serait encore mieux.
Francisco Zarabozo
Bonne solution pour les ajustements rapides
Genaut
1
Vous n'avez pas besoin d'un verrou tel que SET FOREIGN_KEY_CHECKSdéfini par la session (les autres sessions auront toujours la contrainte FK appliquée). C'est parfait pour ajouter / supprimer AUTO_INCREMENT(ce qui ne change pas le type de données de colonne réel), mais cela ne fonctionnera pas si vous essayez de changer le type de données de colonne pour "réel" (disons, de SMALLINT à INT) car vous obtiendrez un 150 FK constraint incorrectly formedlorsque mysql essaie de remplacer l'ancienne table par la nouvelle. Dans ce cas, utilisez la réponse acceptée.
Xenos
-3

Lorsque vous définissez des clés (primaires ou étrangères), vous définissez des contraintes sur la façon dont elles peuvent être utilisées, ce qui limite ce que vous pouvez en faire. Si vous voulez vraiment modifier la colonne, vous pouvez recréer la table sans les contraintes, même si je vous le déconseille. De manière générale, si vous avez une situation dans laquelle vous voulez faire quelque chose, mais qu'elle est bloquée par une contrainte, il est préférable de la résoudre en modifiant ce que vous voulez faire plutôt que la contrainte.

RonaldBarzell
la source
12
C'est une TELLE réponse inutile et inutile!
ajmedway
3
@ajmedway Ensuite, vous pouvez écrire une réponse utile sans blâmer les autres utilisateurs
Je suis la personne la plus stupide
2
@IamtheMostStupidPerson C'est bien mieux que de simplement voter sans commentaire. Au moins, le commentateur peut deviner pourquoi les votes négatifs. Des réponses génériques telles que «mieux vaut prévenir que guérir» ne sont pas utiles.
Csaba Toth