Mysql
 sql >> Base de données >  >> RDS >> Mysql

INSERT IGNORE INTO de MySQL et les clés étrangères

[NOUVELLE RÉPONSE]

Merci à @NeverEndingQueue d'avoir soulevé cette question. Il semble que MySQL ait enfin résolu ce problème. Je ne sais pas dans quelle version ce problème a été corrigé pour la première fois, mais pour l'instant j'ai testé avec la version suivante et le problème n'est plus là :

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                        |
+-------------------------+------------------------------+

Pour être clair :

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

Pour mieux comprendre la signification de cette dernière requête et pourquoi elle montre que le problème est résolu, veuillez continuer avec l'ancienne réponse ci-dessous.

[ANCIENNE RÉPONSE]

Ma solution consiste à contourner le problème et la solution réelle consistera toujours à résoudre le problème au sein de MySQL lui-même.

Les étapes suivantes ont résolu mon problème :

a. Envisagez d'avoir les tableaux et données suivants :

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. Nous devons maintenant supprimer certaines des lignes pour illustrer le problème :

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

c. PROBLÈME : Le problème survient lorsque vous essayez d'insérer les lignes enfants suivantes :

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)

Même si le IGNORE mot-clé est utilisé, mais MySQL annule l'opération demandée car l'erreur générée n'est pas transformée en avertissement (comme il était censé le faire). Maintenant que le problème est évident, voyons comment exécuter la dernière insertion dans l'instruction sans rencontrer d'erreur.

d. SOLUTION : Je vais envelopper l'insert en instruction par d'autres instructions constantes qui ne dépendent ni des enregistrements insérés, ni de leur nombre.

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;

Je sais que ce n'est pas optimal mais tant que MySQL n'a pas résolu le problème, c'est le meilleur que je connaisse. D'autant plus que toutes les instructions peuvent être exécutées en une seule requête si vous utilisez la bibliothèque mysqli en PHP.