Lors de l'exécution d'une instruction INSERT
comportant plusieurs lignes, je souhaite ignorer les entrées en double qui pourraient sinon provoquer un échec. Après quelques recherches, mes options semblent être l’utilisation de:
ON DUPLICATE KEY UPDATE
qui implique une mise à jour inutile à un coût quelconque, ouINSERT IGNORE
qui implique une invitation pour d'autres types d'échec à se glisser sans préavis.Ai-je raison dans ces hypothèses? Quelle est la meilleure façon de simplement ignorer les lignes qui pourraient causer des doublons et de simplement passer aux autres lignes?
Je recommanderais d'utiliser INSERT...ON DUPLICATE KEY UPDATE
.
Si vous utilisez INSERT IGNORE
, la ligne ne sera pas insérée si elle entraîne une clé en double. Mais la déclaration ne générera pas d'erreur. Il génère un avertissement à la place. Ces cas incluent:
PRIMARY KEY
ou UNIQUE
.NOT NULL
.Si vous utilisez REPLACE
, MySQL effectue en fait un DELETE
suivi d'un INSERT
en interne, ce qui a des effets secondaires inattendus:
REPLACE
.DELETE
sont exécutés inutilement.correction:REPLACE
et INSERT...ON DUPLICATE KEY UPDATE
sont des inventions propriétaires non standard spécifiques à MySQL. ANSI SQL 2003 définit une instruction MERGE
qui peut résoudre le même besoin (et davantage), mais MySQL ne prend pas en charge l'instruction MERGE
.
Un utilisateur a tenté de modifier ce message (la modification a été rejetée par les modérateurs). La modification a tenté d'ajouter une revendication selon laquelle INSERT...ON DUPLICATE KEY UPDATE
provoque l'attribution d'un nouvel ID d'auto-incrémentation. Il est vrai que le nouvel identifiant est généré , mais il n'est pas utilisé dans la ligne modifiée.
Voir la démonstration ci-dessous, testée avec Percona Server 5.5.28. La variable de configuration innodb_autoinc_lock_mode=1
(valeur par défaut):
mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 10 |
+----+------+
mysql> show create table foo\G
CREATE TABLE `foo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`u` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1
mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 20 |
+----+------+
mysql> show create table foo\G
CREATE TABLE `foo` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`u` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1
Ce qui précède montre que l’instruction IODKU détecte le doublon et appelle la mise à jour pour modifier la valeur de u
. Notez que AUTO_INCREMENT=3
indique qu'un identifiant a été généré mais non utilisé dans la ligne.
Alors que REPLACE
supprime la ligne d'origine et insère une nouvelle ligne, générant et stockant un nouvel identifiant d'auto-incrémentation:
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 1 | 20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u |
+----+------+
| 3 | 20 |
+----+------+
Au cas où vous voudriez voir ce que tout cela veut dire, voici un coup par coup:
CREATE TABLE `users_partners` (
`uid` int(11) NOT NULL DEFAULT '0',
`pid` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`uid`,`pid`),
KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
La clé primaire est basée sur les deux colonnes de cette table de référence rapide. Une clé primaire nécessite des valeurs uniques.
Commençons:
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected
INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected
notez que ceci a épargné trop de travail supplémentaire en réglant la colonne sur elle-même, aucune mise à jour n'est nécessaire
REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected
et maintenant quelques tests sur plusieurs lignes:
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'
INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected
aucun autre message n'a été généré dans la console et il contient maintenant ces 4 valeurs dans les données de la table. J'ai tout supprimé sauf (1,1) pour pouvoir tester depuis le même terrain
INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected
REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected
Donc là vous l'avez. Etant donné que tout cela a été effectué sur une table fraîche avec pratiquement aucune donnée et qu’elle n’est pas en production, les délais d’exécution étaient microscopiques et sans importance. Quiconque disposant de données du monde réel serait le bienvenu pour y contribuer.
Quelque chose d’important à ajouter: lorsque vous utilisez INSERT IGNORE et que vous avez des violations clés, MySQL n’émet aucun avertissement!
Si vous essayez par exemple d'insérer 100 enregistrements à la fois, dont un en défaut, vous passeriez en mode interactif:
Query OK, 99 rows affected (0.04 sec)
Records: 100 Duplicates: 1 Warnings: 0
Comme vous le voyez: pas d'avertissement! Ce comportement est même décrit à tort dans la documentation officielle de Mysql.
Si votre script doit être informé, si certains enregistrements n'ont pas été ajoutés (en raison de violations de clé), vous devez appeler mysql_info () et l'analyser pour obtenir la valeur "Duplicates".
J'utilise couramment INSERT IGNORE
, et cela ressemble exactement au type de comportement que vous recherchez. Tant que vous saurez que les lignes susceptibles de provoquer des conflits d’index ne seront pas insérées et que vous planifiez votre programme en conséquence, cela ne devrait pas poser de problème.
Je sais que c'est vieux, mais je vais ajouter cette note au cas où quelqu'un d'autre (comme moi) arriverait sur cette page en essayant de trouver des informations sur INSERT..IGNORE.
Comme indiqué ci-dessus, si vous utilisez INSERT..IGNORE, les erreurs qui se produisent lors de l'exécution de l'instruction INSERT sont traitées à la place comme des avertissements.
Une chose qui n’est pas explicitement mentionnée est que INSERT..IGNORE entraînera des valeurs non valides qui seront ajustées aux valeurs les plus proches lorsqu’elles seront insérées (alors que des valeurs non valides provoqueraient l’abandon de la requête si le mot clé IGNORE n’était pas utilisé).
Replace
Into semble être une option. Ou vous pouvez vérifier avec
IF NOT EXISTS(QUERY) Then INSERT
Cela va insérer ou supprimer puis insérer. J'ai tendance à aller d'abord pour un IF NOT EXISTS
.
ON DUPLICATE KEY UPDATE n'est pas réellement dans la norme. C'est à peu près aussi standard que REPLACE. Voir SQL MERGE .
Les deux commandes sont essentiellement des versions à syntaxe alternative des commandes standard.
Danger potentiel d'INSERT IGNORE. Si vous essayez d'insérer une valeur VARCHAR plus longue que la colonne a été définie avec - la valeur sera tronquée et insérée même si le mode strict est activé.
Si vous utilisez insert ignore
, le fait d'avoir une instruction SHOW WARNINGS;
à la fin de votre ensemble de requêtes affichera une table avec tous les avertissements, y compris les ID en double.
Si vous souhaitez insérer dans la table et sur le conflit de la clé primaire ou de l'index unique, il met à jour la ligne en conflit au lieu de l'insérer.
Syntaxe:
insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;
Maintenant, ici, cette déclaration peut sembler différente de ce que vous avez vu précédemment. Cette instruction insert essayant d'insérer une ligne dans table1 avec les valeurs de a et b dans les colonnes column1 et column2, respectivement.
Comprenons cette déclaration en profondeur:
Par exemple: ici, colonne1 est définie comme clé primaire dans table1.
Maintenant, si dans la table1, il n'y a pas de ligne ayant la valeur "a" dans la colonne1. Donc, cette déclaration va insérer une ligne dans la table1.
Maintenant, si dans la table1, il existe une ligne ayant la valeur "a" dans la colonne2. Ainsi, cette instruction mettra à jour la valeur column2 de la ligne avec "c", la valeur column1 étant "a".
Donc, si vous souhaitez insérer une nouvelle ligne, mettez à jour cette ligne sur le conflit de la clé primaire ou de l'index unique.
En savoir plus sur ce lien