416 votes

MySQL : Insérer un enregistrement s'il n'existe pas dans la table

J'essaie d'exécuter la requête suivante :

INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name='value'
);

Mais cela renvoie une erreur. En fait, je ne veux pas insérer un enregistrement si le champ "nom" de l'enregistrement existe déjà dans un autre enregistrement. Comment vérifier si le nouveau nom est unique ?

12 votes

Toutes les réponses actuelles à cette question ou aux doublons supposent que vous pouvez ajouter un index unique. Parfois, la décision repose sur une logique commerciale qui ne peut être imposée à l'ensemble de la table. Par exemple, vous autorisez plusieurs lignes avec une certaine valeur dans une colonne, mais une autre valeur de la colonne ne pourra apparaître que sur une seule ligne. Comment faire ?

546voto

Mike Points 11170

Je ne suis pas en train de suggérer que vous fassiez cela, car le UNIQUE comme suggéré par Piskvor et d'autres est une bien meilleure façon de le faire, mais vous pouvez réellement faire ce que vous essayiez :

CREATE TABLE `table_listnames` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `address` varchar(255) NOT NULL,
  `tele` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

Insérer un enregistrement :

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

Essayez à nouveau d'insérer le même enregistrement :

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

Insérez un autre enregistrement :

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
|  2 | John   | Doe       | 022  |
+----+--------+-----------+------+

Et ainsi de suite...


Mise à jour :

Pour éviter #1060 - Duplicate column name en cas d'égalité de deux valeurs, vous devez nommer les colonnes du SELECT interne :

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+---------+-----------+------+
| id | name    | address   | tele |
+----+---------+-----------+------+
|  1 | Rupert  | Somewhere | 022  |
|  2 | John    | Doe       | 022  |
|  3 | Unknown | Unknown   | 022  |
+----+---------+-----------+------+

19 votes

Merci, cela m'a aidé. Mon problème réel est beaucoup plus complexe et la colonne ne peut tout simplement pas être unique et je ne peux pas dépendre de la clé primaire. Mais c'est exactement ce que je cherchais.

0 votes

Cela fonctionnerait, mais je suis un peu inquiet pour les performances - à moins qu'il y ait un index sur name la sélection la plus interne sera horriblement lente. S'il y a un index, cela devrait fonctionner correctement.

2 votes

@Piskovar : D'accord. @Rupert : vous devriez indexer la colonne à laquelle il est fait référence dans l'instruction select interne ( name dans ce cas), si cela est possible. Notez également que vous pouvez faire SELECT 'John', 'Doe', '022' FROM table_listnames au lieu de SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp - mais cela ne fonctionnera que si table_listnames contient déjà une ou plusieurs lignes. Je doute que la vitesse soit différente, donc ce n'est probablement pas un problème.

303voto

Piskvor Points 46986

INSERT ne permet pas WHERE dans la syntaxe .

Ce que vous pouvez faire : créer un UNIQUE INDEX sur le champ qui doit être unique ( name ), alors utilisez l'un ou l'autre :

  • normal INSERT (et gérer l'erreur si le nom existe déjà)
  • INSERT IGNORE (qui sera échouer en silence provoquer un avertissement (au lieu d'une erreur) si le nom existe déjà)
  • INSERT ... ON DUPLICATE KEY UPDATE (qui exécutera le UPDATE à la fin si le nom existe déjà, voir la documentation )

0 votes

INSERT IGNORE a fonctionné pour moi. C'est la meilleure option, surtout pour les grands ensembles de données.

1 votes

Si vous avez besoin d'obtenir un message d'avertissement, vous pouvez ensuite faire ce qui suit mysql> show warnings\G

2 votes

Ime, le message d'avertissement, en cas de INSERT IGNORE correspond au regex de ^Duplicate entry '.+' for key '.+'$

69voto

Mahbub Tito Points 81

A travaillé :

INSERT INTO users (full_name, login, password) 
  SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS 
  (SELECT login FROM users WHERE login='tito');

0 votes

Cela ne fonctionne que pour les bases de données Oracle, n'est-ce pas ? fr.wikipedia.org/wiki/DUAL_table Cette question porte spécifiquement sur MySQL !

3 votes

Je ne teste pas dans la base de données Oracle. Il fonctionne bien avec MySQL et a été testé.

9 votes

J'ai appris depuis que "dual" est disponible dans MySQL mais pas dans MariaDB, la branche open source de MySQL.

27voto

Montaser El-sawy Points 119
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Voici la requête mysql, qui insère les enregistrements s'ils n'existent pas et ignore les enregistrements similaires existants.

----Untested----

4 votes

Cela ne fonctionne pas pour moi, INSERT IGNORE INTO emAssignedTagsForEvent SET eventId='2',defaultTagId='1' ;

8 votes

On dirait un mélange de syntaxes d'insertion et de mise à jour. Voulez-vous dire INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678); ?

0 votes

@Hobo Du manuel MySQL dev.mysql.com/doc/refman/5.7/fr/insert.html Syntaxe INSERT : Il peut être INSERT [...] IGNORE INTO mytable VALUES ... Ou INSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...

13voto

Brian Hooper Points 8906

Si vous ne pouvez vraiment pas obtenir un index unique sur la table, vous pouvez essayer...

INSERT INTO table_listnames (name, address, tele)
    SELECT 'Rupert', 'Somewhere', '022'
        FROM some_other_table
        WHERE NOT EXISTS (SELECT name
                              FROM table_listnames
                              WHERE name='Rupert')
        LIMIT 1;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X