Mysql
 sql >> Database >  >> RDS >> Mysql

INSERT IGNORE INTO di MySQL e chiavi esterne

[NUOVA RISPOSTA]

Grazie a @NeverEndingQueue per averlo sollevato. Sembra che MySQL abbia finalmente risolto questo problema. Non sono sicuro in quale versione sia stato risolto questo problema, ma in questo momento ho testato con la seguente versione e il problema non è più presente:

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

Per essere chiari:

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

Per comprendere meglio il significato di quest'ultima query e il motivo per cui mostra che il problema è stato risolto, continua con la vecchia risposta di seguito.

[VECCHIA RISPOSTA]

La mia soluzione è una soluzione al problema e la soluzione effettiva risolverà sempre il problema all'interno di MySQL stesso.

I seguenti passaggi hanno risolto il mio problema:

a. Considera di avere le seguenti tabelle e dati:

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. Ora dobbiamo eliminare alcune delle righe per dimostrare il problema:

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

c. PROBLEMA: Il problema sorge quando si tenta di inserire le seguenti righe figlio:

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)

Anche se IGNORE viene utilizzata la parola chiave, ma MySQL annulla l'operazione richiesta perché l'errore generato non viene trasformato in avviso (come dovrebbe). Ora che il problema è evidente, vediamo come eseguire l'ultimo inserimento nell'istruzione senza incorrere in alcun errore.

d. SOLUZIONE: Incorporerò l'inserto nell'istruzione con altre istruzioni costanti che non dipendono né dai record inseriti né dal loro numero.

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;

So che questo non è ottimale, ma finché MySQL non ha risolto il problema, questo è il migliore che conosco. Soprattutto perché tutte le istruzioni possono essere eseguite in una richiesta se usi la libreria mysqli in PHP.