Mysql error 1452: no se puede agregar o actualizar una fila secundaria: falla una restricción de clave externa

237

Tengo un problema un poco extraño. Estoy tratando de agregar una clave externa a una tabla que hace referencia a otra, pero está fallando por alguna razón. Con mi conocimiento limitado de MySQL, lo único que podría ser sospechoso es que hay una clave externa en una tabla diferente que hace referencia a la que estoy tratando de hacer referencia.

He hecho una SHOW CREATE TABLEconsulta en ambas tablas, sourcecodes_tagses la tabla con la clave externa, sourcecodeses la tabla referenciada.

CREATE TABLE `sourcecodes` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `user_id` int(11) unsigned NOT NULL,
 `language_id` int(11) unsigned NOT NULL,
 `category_id` int(11) unsigned NOT NULL,
 `title` varchar(40) CHARACTER SET utf8 NOT NULL,
 `description` text CHARACTER SET utf8 NOT NULL,
 `views` int(11) unsigned NOT NULL,
 `downloads` int(11) unsigned NOT NULL,
 `time_posted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 PRIMARY KEY (`id`),
 KEY `user_id` (`user_id`),
 KEY `language_id` (`language_id`),
 KEY `category_id` (`category_id`),
 CONSTRAINT `sourcecodes_ibfk_3` FOREIGN KEY (`language_id`) REFERENCES `languages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `sourcecodes_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `sourcecodes_ibfk_2` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1

CREATE TABLE `sourcecodes_tags` (
 `sourcecode_id` int(11) unsigned NOT NULL,
 `tag_id` int(11) unsigned NOT NULL,
 KEY `sourcecode_id` (`sourcecode_id`),
 KEY `tag_id` (`tag_id`),
 CONSTRAINT `sourcecodes_tags_ibfk_1` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1

Este es el código que genera el error:

ALTER TABLE sourcecodes_tags ADD FOREIGN KEY (sourcecode_id) REFERENCES sourcecodes (id) ON DELETE CASCADE ON UPDATE CASCADE
Zim
fuente
2
¿también podría publicar su comando de inserción / actualización que da como resultado el error?
Zed
64
¿Están sus tablas vacías cuando agrega esta clave foránea?
Zed
12
intente ejecutar esta consulta para ver si hay alguna sourcecode_id que no sea una identificación real: SELECCIONE sourcecode_id DESDE sourcecodes_tags DONDE sourcecode_id NO ESTÁ (SELECCIONE ID DESDE sourcecodes AS tmp);
Zed
11
Gracias Zed, ese era el problema que una de las tablas tenía datos. Pensando en ello ahora tiene sentido que estuviera fallando porque había cosas que hacían referencia a elementos no existentes, pero nunca lo habría adivinado. ¡Gracias!
Zim
2
¿Por qué falla si la tabla está vacía?
theblackpearl

Respuestas:

226

Es muy probable que su sourcecodes_tagstabla contenga sourcecode_idvalores que ya no existen en su sourcecodestabla. Tienes que deshacerte de esos primero.

Aquí hay una consulta que puede encontrar esos ID:

SELECT DISTINCT sourcecode_id FROM 
   sourcecodes_tags tags LEFT JOIN sourcecodes sc ON tags.sourcecode_id=sc.id 
WHERE sc.id IS NULL;
nos
fuente
UPDATE sourcecodes_tags SET sourcecode_id = NULL WHERE sourcecode_id NOT IN (SELECT id FROM sourcecodes)debería ayudar a deshacerse de esas identificaciones. O si nullno está permitido sourcecode_id, elimine esas filas o agregue esos valores faltantes a la sourcecodestabla.
naXa
Estaba pensando lo mismo pero, para mí SELECT Tchild.id FROM Tchild INNER JOIN Tmain ON Tmain.id = Tchild.fk_id WHERE Tmain.id IS NULLno devuelve nada, ¡así que el problema está en otra parte!
Meloman
Ahh, este fue el problema para mí. Estaba tratando de correr UPDATE `homestead`.`automations` SET `deleted_at`=NULL WHERE deleted_at IS NOT NULL;, lo que no implicaba ninguna clave foránea, así que estaba confundido. Pero el hecho de que mi tabla de contactos no tenía algunos registros a los que se refería la tabla de automatizaciones hizo que arrojara este "Código de error: 1452. No se puede agregar o actualizar una fila secundaria: falla una restricción de clave externa".
Ryan
99

Tuve el mismo problema con mi base de datos MySQL pero finalmente, obtuve una solución que funcionó para mí.
Como en mi tabla todo estaba bien desde el punto de vista de mysql (ambas tablas deberían usar el motor InnoDB y el tipo de datos de cada columna debería ser del mismo tipo que participa en la restricción de clave externa).
Lo único que hice fue deshabilitar la verificación de clave externa y luego habilitarla después de realizar la operación de clave externa.
Pasos que tomé:

SET foreign_key_checks = 0;
alter table tblUsedDestination add constraint f_operatorId foreign key(iOperatorId) references tblOperators (iOperatorId); Query
OK, 8 rows affected (0.23 sec) Records: 8  Duplicates: 0  Warnings: 0
SET foreign_key_checks = 1;
Prakash
fuente
49
Foreign_key_checks están ahí por una razón. Si no puede agregar la clave externa porque viola la restricción, primero debe corregir los datos. Apagar los cheques y luego agregar la clave lo deja en un estado inconsistente. Las comprobaciones de clave externa agregan gastos generales, si no desea utilizarlas, utilice myisam en su lugar.
cs_alumnus
55
@AbuSadatMohammedYasin no, no debería: la pregunta fue "qué está pasando" y esta respuesta simplemente no intenta explicarlo. Como mencionó cs_alumnus, hay un problema mayor: todos los valores nuevos que deberían estar haciendo referencia a otro valor en la otra tabla (como debería hacer una clave externa ) pueden no apuntar a nada, creando un estado inconsistente. La breve y efectiva explicación de Cayetano le permite encontrar qué valores debe actualizar antes de crear la restricción para que no le sorprendan las consultas que deberían devolver valores que deberían existir.
Armfoot
55

Use NOT INpara encontrar dónde las restricciones son limitantes :

SELECT column FROM table WHERE column NOT IN 
(SELECT intended_foreign_key FROM another_table)

entonces, más específicamente:

SELECT sourcecode_id FROM sourcecodes_tags WHERE sourcecode_id NOT IN 
(SELECT id FROM sourcecodes)

EDIT: INy NOT INoperadores son conocidos por ser mucho más rápido que los JOINoperadores, así como mucho más fáciles de construir, y repetir.

Ryan Ward
fuente
1
Entonces, si entiendo esto correctamente, ¿podemos agregar una clave externa a una tabla que ya tiene datos, pero solo si existe una fila secundaria para cada fila en la tabla primaria? Si no hay filas secundarias para cada fila en la tabla principal (que es lo que descubre su consulta), el script de clave externa fallará.
Vincent
@Vincent si por tabla principal te refieres a la tabla a la que se hace referencia, ¡entonces sí! Por lo tanto, con la selección de Cayetano, obtiene todas las filas que necesita actualizar / eliminar de su tabla "secundaria" antes de agregar la nueva restricción (FK). Una vez que todos apuntan a valores en la "otra_tabla", ¡ya está listo!
Armfoot
23

Trunca las tablas y luego intenta agregar la restricción FK .

Sé que esta solución es un poco incómoda, pero funciona al 100%. Pero estoy de acuerdo en que esta no es una solución ideal para lidiar con el problema, pero espero que ayude.

Shankar Damodaran
fuente
44
No es necesario truncar todo. "ACTUALIZAR sourcecodes_tags SET sourcecode_id = NULL DONDE sourcecode_id NOT IN (SELECT id FROM sourcecodes)" debería ser suficiente. O si no se permite nulo en "sourcecode_id", elimine esas filas o agregue los valores faltantes a la tabla "sourcecodes".
Torben
1
A veces, si los datos incrementan el PK de autoincremento, te obliga a truncar.
François Breton el
2
@ShankarDamodaran no está seguro de por qué truncar la tabla funciona, pero esta solución funcionó bien para mí. Pude hacer que mis relaciones funcionaran ... ¡GRACIAS!
MizAkita
@MizAkita funciona porque elimina las filas que no tienen un valor correspondiente en la otra tabla, permitiendo que se cree la nueva restricción. Si solo encuentra esas filas y las actualiza o las elimina (como la sugerencia de Cayetano ), no necesita eliminar las otras filas ...
Armfoot
@Armfoot: tuve este problema al agregar la primera fila a la tabla con clave externa. Así que no tenía filas para buscar.
Krewetka
16

Para mí, este problema fue un poco diferente y muy fácil de verificar y resolver.

Debe asegurarse de que AMBAS tablas sean InnoDB. Si una de las tablas, es decir, la tabla de referencia es MyISAM, la restricción fallará.

    SHOW TABLE STATUS WHERE Name =  't1';

    ALTER TABLE t1 ENGINE=InnoDB;
zmonteca
fuente
14

Esto también ocurre cuando se configura una clave foránea para parent.id en child.column si child.column ya tiene un valor de 0 y ningún valor parent.id es 0

Debería asegurarse de que cada child.column sea NULL o tenga un valor que exista en parent.id

Y ahora que leí la declaración que escribimos, eso es lo que está validando.

fyrye
fuente
14

Tuve el mismo problema hoy. Probé cuatro cosas, algunas de ellas ya mencionadas aquí:

  1. ¿Hay algún valor en su columna secundaria que no exista en la columna primaria (además de NULL, si la columna secundaria es anulable)

  2. ¿Las columnas hijo y padre tienen el mismo tipo de datos?

  3. ¿Hay un índice en la columna principal a la que hace referencia? MySQL parece requerir esto por razones de rendimiento ( http://dev.mysql.com/doc/refman/5.5/en/create-table-foreign-keys.html )

  4. Y este lo resolvió para mí: ¿Ambas tablas tienen una clasificación idéntica?

Tenía una mesa en UTF-8 y la otra en iso-algo. Eso no funcionó. Después de cambiar la clasificación iso-table a UTF-8, las restricciones podrían agregarse sin problemas. En mi caso, phpMyAdmin ni siquiera mostró la tabla secundaria en codificación iso en el menú desplegable para crear la restricción de clave externa.

Michael Helwig
fuente
7

Parece que hay un valor no válido para la línea de columna 0 que no es una clave externa válida, por lo que MySQL no puede establecer una restricción de clave externa para ella.

Puedes seguir estos pasos:

  1. Suelte la columna para la que ha intentado establecer la restricción FK.

  2. Agréguelo nuevamente y establezca su valor predeterminado como NULL.

  3. Intente establecer una restricción de clave externa para él nuevamente.

Milad Rahimi
fuente
5

Tenía el mismo problema, revisé las filas de mis tablas y descubrí que había cierta incompatibilidad con el valor de los campos que quería definir una clave externa. Corregí esos valores, intenté nuevamente y el problema se resolvió.

Mostafa -T
fuente
4

Termino borrando todos los datos de mi tabla y ejecuto alter nuevamente. Funciona. No es el brillante, pero ahorra mucho tiempo, especialmente su aplicación aún está en etapa de desarrollo sin ningún dato del cliente.

VHanded
fuente
4

prueba esto

SET foreign_key_checks = 0;

ALTER TABLE sourcecodes_tags ADD FOREIGN KEY (sourcecode_id) REFERENCES sourcecodes (id) ON DELETE CASCADE ON UPDATE CASCADE

SET foreign_key_checks = 1;
Carlo Guevarra
fuente
2

Tuve exactamente el mismo problema unas tres veces diferentes. En cada caso fue porque uno (o más) de mis registros no se ajustaba a la nueva clave externa. Es posible que desee actualizar sus registros existentes para seguir las restricciones de sintaxis de la clave externa antes de intentar agregar la clave en sí. El siguiente ejemplo generalmente debería aislar los registros de problemas:

SELECT * FROM (tablename)
    WHERE (candidate key) <> (proposed foreign key value) 
        AND (candidate key) <> (next proposed foreign key value)

repita AND (candidate key) <> (next proposed foreign key value)dentro de su consulta para cada valor en la clave externa.

Si tiene una tonelada de registros, esto puede ser difícil, pero si su tabla es razonablemente pequeña, no debería demorar demasiado. No soy súper sorprendente en la sintaxis SQL, pero esto siempre me ha aislado el problema.

mopsyd
fuente
2

Vacíe los datos de ambas tablas y ejecute el comando. Funcionará.

vijayrana
fuente
VHanded dio la misma respuesta hace 3 años. Esperemos que no haya datos importantes en las tablas ...
xlecoustillier
1

Estaba preparando estas soluciones y este ejemplo puede ayudar.

Mi base de datos tiene dos tablas (email y credit_card) con claves principales para sus ID. Otra tabla (cliente) se refiere a los ID de esta tabla como claves foráneas. Tengo una razón para tener el correo electrónico aparte de los datos del cliente.

Primero inserto los datos de la fila para las tablas a las que se hace referencia (correo electrónico, tarjeta de crédito), luego obtiene la ID para cada una, esas ID son necesarias en la tercera tabla (cliente).

Si no inserta primero las filas en las tablas referenciadas, MySQL no podrá hacer las correspondencias cuando inserte una nueva fila en la tercera tabla que haga referencia a las claves foráneas.

Si primero inserta las filas referenciadas para las tablas referenciadas, luego la fila que hace referencia a claves foráneas, no se produce ningún error.

Espero que esto ayude.

Sustancia MX
fuente
mysql> insertar en valores de correo electrónico (correo electrónico) ('[email protected]'); mysql> insertar en valores ndtc (ndtc, año, mes) ('1111222233334444', '2000', '01'); mysql> insertar en los valores del cliente (nombres, apellidos, teléfono, idNDTC, idEmail) ('mi nombre', 'myapp', '5555555555', 1,1);
SubstanceMX
1

Asegúrese de que el valor esté en la otra tabla; de lo contrario, obtendrá este error en la columna correspondiente asignada.

Por lo tanto, si se asigna una columna a una ID de fila de otra tabla, asegúrese de que haya una fila en la tabla; de lo contrario, aparecerá este error.

Kingsley Mitchell
fuente
1

puedes probar este ejemplo

 START TRANSACTION;
 SET foreign_key_checks = 0;
 ALTER TABLE `job_definers` ADD CONSTRAINT `job_cities_foreign` FOREIGN KEY 
 (`job_cities`) REFERENCES `drop_down_lists`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
 SET foreign_key_checks = 1;
 COMMIT;

Nota: si está utilizando phpmyadmin, simplemente desmarque Habilitar comprobaciones de clave externa

como ejemplo ingrese la descripción de la imagen aquí

Espero que esta solución solucione su problema :)

Fouad Mekkey
fuente
1

Solo necesita responder una pregunta:

¿Su tabla ya está almacenando datos? (Especialmente la tabla incluía clave foránea).

Si la respuesta es sí, entonces lo único que debe hacer es eliminar todos los registros, entonces puede agregar cualquier clave externa a su tabla.

Instrucciones de eliminación: de la tabla secundaria (que incluye la tabla de clave externa) a la tabla primaria.

La razón por la que no puede agregar una clave externa después de las entradas de datos se debe a la inconsistencia de la tabla, ¿cómo va a lidiar con una nueva clave externa en la tabla que llena los datos anteriores?

Si la respuesta es no, entonces siga otras instrucciones.

nadie
fuente
0
UPDATE sourcecodes_tags
SET sourcecode_id = NULL
WHERE sourcecode_id NOT IN (
  SELECT id FROM sourcecodes);

debería ayudar a deshacerse de esas identificaciones. O si nullno está permitido sourcecode_id, elimine esas filas o agregue esos valores faltantes a la sourcecodestabla.

naXa
fuente
0

Tuve el mismo problema y encontré la solución, colocando en NULLlugar de NOT NULLen la columna de clave externa. Aquí hay una consulta:

ALTER TABLE `db`.`table1`
ADD COLUMN `col_table2_fk` INT UNSIGNED NULL,
ADD INDEX `col_table2_fk_idx` (`col_table2_fk` ASC),
ADD CONSTRAINT `col_table2_fk1`
FOREIGN KEY (`col_table2_fk`)
REFERENCES `db`.`table2` (`table2_id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION;

MySQL ha ejecutado esta consulta!

akelec
fuente
0

En mi caso, creé una nueva tabla con la misma estructura, creé las relaciones con las otras tablas, luego extraje los datos en CSV de la tabla anterior que tiene el problema, luego importé el CSV a la nueva tabla y deshabilité la comprobación de clave externa y deshabilitó la interrupción de importación, todos mis datos se insertan en la nueva tabla que no tiene ningún problema con éxito, luego se elimina la tabla anterior.

Funcionó para mi.

Shamal Sabah
fuente