sql >> Base de Datos >  >> RDS >> Mysql

MySQL's INSERT IGNORE INTO y claves foráneas

[NUEVA RESPUESTA]

Gracias a @NeverEndingQueue por mencionar esto. Parece que MySQL finalmente solucionó este problema. No estoy seguro de en qué versión se solucionó este problema por primera vez, pero ahora probé con la siguiente versión y el problema ya no existe:

mysql> SHOW VARIABLES LIKE "%version%";
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 5.7.22                       |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| tls_version             | TLSv1,TLSv1.1                |
| version                 | 5.7.22                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+

Para ser claro:

mysql> INSERT IGNORE INTO child
    -> VALUES
    ->     (NULL, 1)
    ->     , (NULL, 2)
    ->     , (NULL, 3)
    ->     , (NULL, 4)
    ->     , (NULL, 5)
    ->     , (NULL, 6);
Query OK, 4 rows affected, 2 warnings (0.03 sec)
Records: 6  Duplicates: 2  Warnings: 2

Para comprender mejor el significado de esta última consulta y por qué muestra que el problema está solucionado, continúe con la respuesta anterior a continuación.

[RESPUESTA ANTIGUA]

Mi solución es una solución al problema y la solución real siempre será solucionar el problema dentro de MySQL.

Los siguientes pasos resolvieron mi problema:

a. Considere tener las siguientes tablas y datos:

mysql>
CREATE TABLE parent (id INT AUTO_INCREMENT NOT NULL
                     , PRIMARY KEY (id)
) ENGINE=INNODB;

mysql>
CREATE TABLE child (id INT AUTO_INCREMENT
                    , parent_id INT
                    , INDEX par_ind (parent_id)
                    , PRIMARY KEY (id)
                    , FOREIGN KEY (parent_id) REFERENCES parent(id)
                        ON DELETE CASCADE
                        ON UPDATE CASCADE
) ENGINE=INNODB;

mysql>
INSERT INTO parent
VALUES (NULL), (NULL), (NULL), (NULL), (NULL), (NULL);

mysql>
SELECT * FROM parent;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
|  4 |
|  5 |
|  6 |
+----+

b. Ahora necesitamos eliminar algunas de las filas para demostrar el problema:

mysql>
DELETE FROM parent WHERE id IN (3, 5);

c. PROBLEMA: El problema surge cuando intenta insertar las siguientes filas secundarias:

mysql>
INSERT IGNORE INTO child
VALUES
    (NULL, 1)
    , (NULL, 2)
    , (NULL, 3)
    , (NULL, 4)
    , (NULL, 5)
    , (NULL, 6);

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint f
ails (`test`.`child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY (`parent_id`) REFERE
NCES `parent` (`id`) ON DELETE CASCADE ON UPDATE CASCADE)

mysql>
SELECT * FROM child;
Empty set (0.00 sec)

Aunque el IGNORE Se usa la palabra clave, pero MySQL cancela la operación solicitada porque el error generado no se convierte en una advertencia (como se suponía). Ahora que el problema es obvio, veamos cómo podemos ejecutar la última inserción en la declaración sin encontrar ningún error.

d. SOLUCIÓN: Voy a envolver la declaración de inserción en otras declaraciones constantes que no dependen de los registros insertados ni de su número.

mysql>
SET FOREIGN_KEY_CHECKS = 0;

mysql>
INSERT INTO child
VALUES
    (NULL, 1)
    , (NULL, 2)
    , (NULL, 3)
    , (NULL, 4)
    , (NULL, 5)
    , (NULL, 6);

mysql>
DELETE FROM child WHERE parent_id NOT IN (SELECT id FROM parent);

mysql>
SET FOREIGN_KEY_CHECKS = 1;

Sé que esto no es óptimo, pero mientras MySQL no haya solucionado el problema, esto es lo mejor que conozco. Especialmente porque todas las declaraciones se pueden ejecutar en una sola solicitud si usa la biblioteca mysqli en PHP.