Impossibile modificare la colonna utilizzata in un vincolo di chiave esterna


111

Ho ricevuto questo errore quando stavo cercando di modificare la mia tabella.

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

Ecco il mio CREATE TABLE STATEMENT che è stato eseguito correttamente.

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

Quindi ho provato a eseguire questa istruzione e ho ricevuto l'errore di cui sopra.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

4
L'esempio sopra è tratto dal libro "Learning SQL, 2nd edition". Spero che l'autore, Alan Beaulieu, apporti delle correzioni.
Dmitry

Risposte:


126

Il tipo e la definizione del campo e del riferimento della chiave esterna devono essere uguali. Ciò significa che la tua chiave esterna non consente la modifica del tipo di campo.

Una soluzione potrebbe essere questa:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

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

Ora puoi cambiare person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

ricrea la chiave esterna

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

UNLOCK TABLES;

EDIT: Aggiunti blocchi sopra, grazie ai commenti

Durante questa operazione è necessario impedire la scrittura nel database, altrimenti si rischiano problemi di integrità dei dati.

Ho aggiunto un blocco in scrittura sopra

Tutte le query di scrittura in qualsiasi altra sessione diversa dalla tua ( INSERT, UPDATE, DELETE) aspetteranno fino al timeout o UNLOCK TABLES; viene eseguito

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

EDIT 2: OP ha chiesto una spiegazione più dettagliata della riga "Il tipo e la definizione del campo e del riferimento della chiave esterna devono essere uguali. Ciò significa che la tua chiave esterna non consente la modifica del tipo del tuo campo."

Da MySQL 5.5 Reference Manual: FOREIGN KEY Constraints

Le colonne corrispondenti nella chiave esterna e nella chiave a cui si fa riferimento devono avere tipi di dati interni simili all'interno di InnoDB in modo che possano essere confrontati senza una conversione del tipo. La dimensione e il segno dei tipi interi devono essere gli stessi. La lunghezza dei tipi di stringa non deve essere la stessa. Per le colonne di stringhe non binarie (caratteri), il set di caratteri e le regole di confronto devono essere gli stessi.


1
Non dimenticare di utilizzare una transazione per questo. Altrimenti il ​​tuo database potrebbe essere danneggiato.
Francois Bourgeois

5
Buon punto, sfortunatamente MySQL non supporta le transazioni intorno alle istruzioni DDL. Le transazioni aperte vengono confermate prima che venga eseguita una query DDL, vedere dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim

2
L'istruzione corretta per ricreare una chiave esterna sarebbe: ALTER TALE favourite_food ADD CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCES person (id);
Felizardo

1
Perché modifichi person_idsubito dopo aver rilasciato la chiave esterna? Sembra che tu non abbia cambiato nulla poiché è già un file SMALLINT UNSIGNED.
Dennis Subachev

1
Non sappiamo cosa fosse poiché ha pubblicato solo la struttura della tabella di riferimento. Innodb ha int come tipo interno, smallint ecc.Sono solo scorciatoie
Michel Feldheim

198

Puoi disattivare i controlli delle chiavi esterne:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Assicurati di NON usarlo in produzione e di avere un backup.


1
Sembra essere una soluzione molto insicura. Può potenzialmente portare alla perdita di integrità dei dati?
hrust

@Synaps - sì, potrebbe se stai eliminando / aggiornando / inserendo. la perdita di dati non si verificherà se stai solo modificando una tabella o se stai eseguendo il seeding del tuo db, d'altra parte dovresti convalidare manualmente i tuoi dati (poiché rimuovi i vincoli)
Dementic

2
Questa soluzione è ottima e puoi usarla in produzione se imposti blocchi di scrittura dell'annuncio prima di modificare i tuoi dati e poi uno sblocco quando hai finito. Usare un file sql per apportare le modifiche nel più breve tempo possibile sarebbe ancora meglio.
Francisco Zarabozo

Buona soluzione per ritocchi veloci
Genaut

1
Non è necessario un blocco in quanto nell'ambito della SET FOREIGN_KEY_CHECKSsessione (ad altre sessioni verrà comunque applicato il vincolo FK). È perfetto per aggiungere / rimuovere AUTO_INCREMENT(che non cambia il tipo di dati della colonna effettiva), ma non funzionerà se provi a cambiare il tipo di dati della colonna per "reale" (ad esempio, da SMALLINT a INT) poiché otterrai un valore legittimo 150 FK constraint incorrectly formedquando mysql cerca di sostituire la vecchia tabella con quella nuova. In tal caso, utilizzare la risposta accettata.
Xenos

-3

Quando imposti le chiavi (primarie o esterne) imposti dei vincoli su come possono essere utilizzate, il che a sua volta limita ciò che puoi fare con esse. Se vuoi davvero modificare la colonna, puoi ricreare la tabella senza i vincoli, anche se ti consiglio di non farlo. In generale, se hai una situazione in cui vuoi fare qualcosa, ma è bloccata da un vincolo, è meglio risolverla cambiando ciò che vuoi fare piuttosto che il vincolo.


12
Questo è TALI una, risposta inutile inutile!
ajmedway

3
@ajmedway Quindi puoi scrivere una risposta utile senza incolpare gli altri utenti
Sono la persona più stupida

2
@IamtheMostStupidPerson È molto meglio che semplicemente downvoting senza un commento. Almeno il commentatore può indovinare il motivo dei voti negativi. Tali risposte generiche come "è meglio prevenire che curare" non sono utili.
Csaba Toth
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.