No se puede cambiar la columna utilizada en una restricción de clave externa

111

Recibí este error cuando intentaba modificar mi tabla.

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

Aquí está mi DECLARACIÓN DE CREAR TABLA que se ejecutó correctamente.

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

Luego intenté ejecutar esta declaración y obtuve el error anterior.

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;
theJava
fuente
4
El ejemplo anterior es del libro "Learning SQL, 2nd edition". Espero que el autor, Alan Beaulieu, haga correcciones.
Dmitry

Respuestas:

126

El tipo y la definición del campo de clave externa y la referencia deben ser iguales. Esto significa que su clave externa no permite cambiar el tipo de su campo.

Una solución sería esta:

LOCK TABLES 
    favorite_food WRITE,
    person WRITE;

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

Ahora puedes cambiar tu person_id

ALTER TABLE person MODIFY person_id SMALLINT UNSIGNED AUTO_INCREMENT;

recrear la clave externa

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

UNLOCK TABLES;

EDITAR: bloqueos agregados arriba, gracias a los comentarios

Debe prohibir la escritura en la base de datos mientras hace esto, de lo contrario corre el riesgo de problemas de integridad de datos.

Agregué un bloqueo de escritura arriba

Todas las consultas de escritura en cualquier otra sesión que no sea la suya ( INSERT, UPDATE, DELETE) esperarán hasta el tiempo de espera o UNLOCK TABLES; es ejecutado

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

EDICIÓN 2: OP pidió una explicación más detallada de la línea "El tipo y la definición del campo de clave externa y la referencia deben ser iguales. Esto significa que su clave externa no permite cambiar el tipo de su campo".

De MySQL 5.5 Reference Manual: FOREIGN KEY Restricciones

Las columnas correspondientes en la clave externa y la clave referenciada deben tener tipos de datos internos similares dentro de InnoDB para que puedan compararse sin una conversión de tipo. El tamaño y el signo de los tipos enteros deben ser iguales. No es necesario que la longitud de los tipos de cadenas sea la misma. Para columnas de cadenas no binarias (de caracteres), el conjunto de caracteres y la intercalación deben ser iguales.

Michel Feldheim
fuente
1
No olvide utilizar una transacción para esto. De lo contrario, su base de datos podría dañarse.
Francois Bourgeois
5
Buen punto, lamentablemente MySQL no admite transacciones en torno a declaraciones DDL. Las transacciones abiertas se confirman antes de que se ejecute una consulta DDL, consulte dev.mysql.com/doc/refman/5.5/en/implicit-commit.html
Michel Feldheim
2
La declaración correcta para recrear una clave externa sería: ALTER TALE favorite_food AÑADIR CONTRAINT fk_fav_food_person_id FOREIGN KEY (person_id) REFERENCIAS person (id);
Felizardo
1
¿Por qué modifica person_idjusto después de soltar la clave externa? Parece que no cambiaste nada porque ya es un SMALLINT UNSIGNED.
Dennis Subachev
1
No sabemos qué fue, ya que solo publicó la estructura de la tabla de referencia. Innodb tiene int como tipo interno, smallint, etc.son accesos directos únicamente
Michel Feldheim
198

Puede desactivar las comprobaciones de claves externas:

SET FOREIGN_KEY_CHECKS = 0;

/* DO WHAT YOU NEED HERE */

SET FOREIGN_KEY_CHECKS = 1;

Asegúrese de NO usar esto en producción y tener una copia de seguridad.

Dementico
fuente
1
Parece ser una solución muy insegura. ¿Puede potencialmente conducir a la pérdida de la integridad de los datos?
hrust
@Synaps: sí, podría hacerlo si está eliminando / actualizando / insertando. La pérdida de datos no ocurrirá si solo está modificando una tabla o sembrando su base de datos, por otro lado, debe validar manualmente sus datos (ya que elimina las restricciones)
Dementic
2
Esta solución es excelente y puede usarla en producción si bloquea la escritura de anuncios antes de modificar sus datos y luego un desbloqueo cuando haya terminado. Usar un archivo sql para realizar los cambios en el menor tiempo posible sería aún mejor.
Francisco Zarabozo
Buena solución para ajustes rápidos
Genaut
1
No necesita un bloqueo, ya que SET FOREIGN_KEY_CHECKSestá en el ámbito de la sesión (otras sesiones aún tendrán la restricción FK aplicada). Es perfecto para agregar / eliminar AUTO_INCREMENT(lo que no cambia el tipo de datos de la columna real), pero no funcionará si intenta cambiar el tipo de datos de la columna por "real" (por ejemplo, de SMALLINT a INT), ya que obtendrá un legítimo 150 FK constraint incorrectly formedcuando mysql intenta reemplazar la tabla anterior por la nueva. En tal caso, utilice la respuesta aceptada.
Xenos
-3

Cuando configura claves (primarias o externas), establece restricciones sobre cómo se pueden usar, lo que a su vez limita lo que puede hacer con ellas. Si realmente desea modificar la columna, puede volver a crear la tabla sin las restricciones, aunque no lo recomiendo. En términos generales, si tiene una situación en la que desea hacer algo, pero está bloqueada por una restricción, es mejor resolverla cambiando lo que desea hacer en lugar de la restricción.

Ronald Barzell
fuente
12
Esta es TAL una respuesta inútil, inútil!
ajmedway
3
@ajmedway Entonces puede escribir una respuesta útil sin culpar a los otros usuarios
soy la persona más estúpida
2
@IamtheMostStupidPerson Es mucho mejor que simplemente votar en contra sin un comentario. Al menos el comentarista puede adivinar por qué los votos negativos. Respuestas genéricas como "es mejor prevenir que curar" no son útiles.
Csaba Toth