Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

INSERT IGNORE INTO do MySQL e chaves estrangeiras


[NOVA RESPOSTA]

Obrigado a @NeverEndingQueue por trazer isso à tona. Parece que o MySQL finalmente corrigiu esse problema. Não tenho certeza em qual versão esse problema foi corrigido pela primeira vez, mas agora testei com a seguinte versão e o problema não existe mais:
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 entender melhor o significado desta última consulta e por que ela mostra que o problema foi corrigido, continue com a resposta antiga abaixo.

[RESPOSTA ANTIGA]

Minha solução é uma solução para o problema e a solução real sempre será corrigir o problema dentro do próprio MySQL.

As etapas a seguir resolveram meu problema:

a. Considere ter as seguintes tabelas e dados:
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. Agora precisamos deletar algumas das linhas para demonstrar o problema:
mysql>
DELETE FROM parent WHERE id IN (3, 5);

c. PROBLEMA: O problema surge quando você tenta inserir as seguintes linhas filhas:
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)

Mesmo que o IGNORE A palavra-chave é usada, mas o MySQL cancela a operação solicitada porque o erro gerado não é transformado em aviso (como deveria). Agora que o problema é óbvio, vamos ver como podemos executar a última instrução insert into sem enfrentar nenhum erro.

d. SOLUÇÃO: Vou envolver a inserção na instrução por algumas outras instruções constantes que não dependem dos registros inseridos nem de seu 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;

Eu sei que isso não é o ideal, mas desde que o MySQL não tenha corrigido o problema, isso é o melhor que conheço. Especialmente porque todas as instruções podem ser executadas em uma solicitação se você usar a biblioteca mysqli em PHP.