MySQL tiene una extensión al lenguaje SQL muy útil, REPLACE, que es una combinación de INSERT y UPDATE: si en la tabla existe una fila con la misma primary key que la que vamos a insertar, MySQL actualiza la fila con los nuevos datos; si no existe, la introduce. Es muy útil porque así no tenemos que tener una sentencia diferente para insertar un nuevo registro (INSERT) o modificar uno existente (UPDATE), y esto hace que el código sea más sencillo y limpio.
Pero tiene truco. Como dicen en la documentación:
REPLACE works exactly like INSERT, except that if an old row in the table has the same value as a new row for a PRIMARY KEY or a UNIQUE index, the old row is deleted before the new row is inserted.
MySQL lo que hace es insertar la fila si no existe esa primary key, pero si existe borra la fila existente y a continuación la inserta.
Esto no es un problema hasta que utilizamos foreign keys: si hacemos REPLACE en una tabla A y hay otra tabla B que tiene una foreign key con ON DELETE CASCADE en A.id, como MySQL elimina la fila de A automáticamente se eliminan todas las filas de B que hacen referencia a ese A.id, y eso es algo que no queremos.
La solución para esto es desactivar la comprobación de foreign keys antes de hacer el REPLACE, y activarlo una vez hecho:
SET FOREIGN_KEY_CHECKS = 0; REPLACE INTO tabla_A (campo1, campo2, ... campoN) VALUES (valor1, valor2, ... valorN); SET FOREIGN_KEY_CHECKS = 1;
De esta forma mantenemos el comportamiento habitual de las foreign keys para borrado de filas y actualización de índices pero lo anulamos cuando queramos usar REPLACE.