web-dev-qa-db-fra.com

Comment "insérer si pas existe" dans MySQL?

J'ai commencé par googler et j'ai trouvé ceci article qui parle de tables mutex.

J'ai une table avec environ 14 millions de disques. Si je veux ajouter plus de données dans le même format, y a-t-il un moyen de s'assurer que l'enregistrement que je veux insérer n'existe pas déjà sans utiliser une paire de requêtes (c'est-à-dire, une requête à vérifier et une à insérer si le jeu de résultats est vide)?

Une contrainte unique sur un champ garantit-elle que le insert échouera s'il est déjà présent?

Il semble qu'avec simplement une contrainte, lorsque je publie l'insertion via php, le script croie.

762
warren

utiliser INSERT IGNORE INTO table

voir http://bogdan.org.ua/2007/10/18/mysql-insert-if-not-exists-syntax.html

il y a aussi la syntaxe INSERT … ON DUPLICATE KEY UPDATE, vous pouvez trouver des explications sur dev.mysql.com


Publiez sur bogdan.org.ua selon le cache Web de Google :

18 octobre 2007

Pour commencer: à partir de la dernière version de MySQL, la syntaxe présentée dans le titre n’est plus possible. Cependant, il existe plusieurs moyens très simples d'accomplir ce qui est attendu à l'aide des fonctionnalités existantes.

Il existe 3 solutions possibles: utiliser INSERT IGNORE, REPLACE ou INSERT… ON DUPLICATE KEY UPDATE.

Imaginons que nous ayons une table:

CREATE TABLE `transcripts` (
`ensembl_transcript_id` varchar(20) NOT NULL,
`transcript_chrom_start` int(10) unsigned NOT NULL,
`transcript_chrom_end` int(10) unsigned NOT NULL,
PRIMARY KEY (`ensembl_transcript_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Imaginons maintenant que nous ayons un pipeline automatique important des métadonnées de transcription de Ensembl et que, pour diverses raisons, le pipeline puisse être interrompu à n'importe quelle étape de son exécution. Par conséquent, nous devons nous assurer de deux choses: 1) les exécutions répétées du pipeline ne détruiront pas notre base de données, et 2) les exécutions répétées ne mourront pas en raison d’erreurs de "clé primaire dupliquée".

Méthode 1: utiliser REPLACE

C'est très simple:

REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Si le dossier existe, il sera écrasé. s'il n'existe pas encore, il sera créé. Toutefois, l’utilisation de cette méthode n’est pas efficace dans notre cas: nous n’avons pas besoin d’écraser les enregistrements existants, c’est bien de les ignorer.

Méthode 2: utilisation de INSERT IGNORE Aussi très simple:

INSERT IGNORE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Ici, si "ensembl_transcript_id" est déjà présent dans la base de données, il sera silencieusement ignoré (ignoré). (Pour être plus précis, voici une citation du manuel de référence MySQL: "Si vous utilisez le mot clé IGNORE, les erreurs qui se produisent lors de l’exécution de l’instruction INSERT sont traitées à la place comme des avertissements. Par exemple, sans IGNORE, une ligne qui duplique un index UNIQUE existant. ou la valeur PRIMARY KEY dans la table provoque une erreur de clé en double et l’instruction est abandonnée. ”.) Si l’enregistrement n’existe pas encore, il sera créé.

Cette seconde méthode présente plusieurs faiblesses potentielles, notamment l'absence d'interruption de la requête en cas de survenue d'un autre problème (voir le manuel). Ainsi, il doit être utilisé s'il a déjà été testé sans le mot clé IGNORE.

Il y a une autre option: utiliser la syntaxe INSERT … ON DUPLICATE KEY UPDATE et dans la partie UPDATE, ne rien faire, effectuer une opération vide (vide), comme calculer 0 + 0 (Geoffray suggère de faire l'affectation id = id pour le moteur d'optimisation MySQL ignorer cette opération). L'avantage de cette méthode est qu'elle ignore uniquement les événements de clé en double et continue d'abandonner d'autres erreurs.

Pour finir, ce message a été inspiré par Xaprb. Je vous conseillerais également de consulter son autre article sur la rédaction de requêtes SQL flexibles.

744
knittl

Solution:

INSERT INTO `table` (`value1`, `value2`) 
SELECT 'stuff for value1', 'stuff for value2' FROM DUAL 
WHERE NOT EXISTS (SELECT * FROM `table` 
      WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1) 

Explication:

La requête la plus profonde

SELECT * FROM `table` 
      WHERE `value1`='stuff for value1' AND `value2`='stuff for value2' LIMIT 1

utilisé comme condition WHERE NOT EXISTS- détecte s'il existe déjà une ligne contenant les données à insérer. Après qu'une ligne de ce type soit trouvée, la requête peut s'arrêter, d'où le LIMIT 1 (micro-optimisation, peut être omis).

La requête intermédiaire

SELECT 'stuff for value1', 'stuff for value2' FROM DUAL

représente les valeurs à insérer. DUAL fait référence à un tableau spécial à une ligne, une colonne présent par défaut dans toutes les bases de données Oracle (voir https://en.wikipedia.org/wiki/DUAL_table ). Sur un serveur MySQL version 5.7.26, une requête valide m'a été fournie en omettant FROM DUAL, mais les versions antérieures (comme 5.5.60) semblent nécessiter les informations FROM. En utilisant WHERE NOT EXISTS, la requête intermédiaire renvoie un ensemble de résultats vide si la requête la plus interne a trouvé des données correspondantes.

La requête externe

INSERT INTO `table` (`value1`, `value2`) 

insère les données, le cas échéant, est renvoyé par la requête intermédiaire.

177
Server

sur la mise à jour des clés dupliquées , ou insert ignore peuvent être des solutions viables avec MySQL.


Exemple de sur une mise à jour de clé dupliquée mise à jour basée sur mysql.com

INSERT INTO table (a,b,c) VALUES (1,2,3)
  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE table SET c=c+1 WHERE a=1;

Exemple de insert ignore basé sur mysql.com

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    {VALUES | VALUE} ({expr | DEFAULT},...),(...),...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

Ou:

INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name
    SET col_name={expr | DEFAULT}, ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]

Ou:

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
    [INTO] tbl_name [(col_name,...)]
    SELECT ...
    [ ON DUPLICATE KEY UPDATE
      col_name=expr
        [, col_name=expr] ... ]
53
Zed

Toute contrainte simple devrait faire l'affaire, si une exception est acceptable. Exemples :

  • clé primaire sinon substitut
  • contrainte unique sur une colonne
  • contrainte unique multi-colonnes

Désolé, cela semble d'une simplicité trompeuse. Je sais que le lien que vous partagez avec nous semble très mauvais. ;

Mais je ne donne jamais cette réponse, car elle semble répondre à vos besoins. (Sinon, cela pourrait déclencher la mise à jour de vos besoins, ce qui serait également une bonne chose).

--- (Edited: Si un insert casse la contrainte unique de la base de données, une exception est levée au niveau de la base de données, relayée par le pilote. Cela arrêtera certainement votre script, avec un échec. Il doit être possible dans PHP de traiter ce cas ...

24
KLE

Voici une fonction PHP qui insérera une ligne uniquement si toutes les valeurs de colonnes spécifiées n'existent pas déjà dans la table.

  • Si l'une des colonnes diffère, la ligne sera ajoutée.

  • Si la table est vide, la ligne sera ajoutée.

  • S'il existe une ligne où toutes les colonnes spécifiées ont les valeurs spécifiées, la ligne ne sera pas ajoutée.

    function insert_unique($table, $vars)
    {
      if (count($vars)) {
        $table = mysql_real_escape_string($table);
        $vars = array_map('mysql_real_escape_string', $vars);
    
        $req = "INSERT INTO `$table` (`". join('`, `', array_keys($vars)) ."`) ";
        $req .= "SELECT '". join("', '", $vars) ."' FROM DUAL ";
        $req .= "WHERE NOT EXISTS (SELECT 1 FROM `$table` WHERE ";
    
        foreach ($vars AS $col => $val)
          $req .= "`$col`='$val' AND ";
    
        $req = substr($req, 0, -5) . ") LIMIT 1";
    
        $res = mysql_query($req) OR die();
        return mysql_insert_id();
      }
    
      return False;
    }
    

Exemple d'utilisation:

<?php
insert_unique('mytable', array(
  'mycolumn1' => 'myvalue1',
  'mycolumn2' => 'myvalue2',
  'mycolumn3' => 'myvalue3'
  )
);
?>
18
Jrm
REPLACE INTO `transcripts`
SET `ensembl_transcript_id` = 'ENSORGT00000000001',
`transcript_chrom_start` = 12345,
`transcript_chrom_end` = 12678;

Si le dossier existe, il sera écrasé. s'il n'existe pas encore, il sera créé.

18
Rocio

Essayez ce qui suit:

IF (SELECT COUNT(*) FROM beta WHERE name = 'John' > 0)
  UPDATE alfa SET c1=(SELECT id FROM beta WHERE name = 'John')
ELSE
BEGIN
  INSERT INTO beta (name) VALUES ('John')
  INSERT INTO alfa (c1) VALUES (LAST_INSERT_ID())
END
17
Jeb's

Plusieurs réponses expliquent comment résoudre ce problème si vous disposez d'un index UNIQUE que vous pouvez vérifier avec ON DUPLICATE KEY ou INSERT IGNORE. Ce n'est pas toujours le cas, et comme UNIQUE a une contrainte de longueur (1000 octets), vous ne pourrez peut-être pas changer cela. Par exemple, je devais travailler avec des métadonnées dans WordPress (wp_postmeta).

Je l'ai finalement résolu avec deux requêtes:

UPDATE wp_postmeta SET meta_value = ? WHERE meta_key = ? AND post_id = ?;
INSERT INTO wp_postmeta (post_id, meta_key, meta_value) SELECT DISTINCT ?, ?, ? FROM wp_postmeta WHERE NOT EXISTS(SELECT * FROM wp_postmeta WHERE meta_key = ? AND post_id = ?);

La requête 1 est une requête normale UPDATE sans effet lorsque l'ensemble de données en question n'y est pas. La requête 2 est une INSERT qui dépend d’un NOT EXISTS, c’est-à-dire que INSERT n’est exécutée que lorsque le jeu de données n’existe pas.

5
wortwart

Essayer:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
if($countrows == '1')
{
  // Exist 
}
else
{
 // .... Not exist
}

Ou tu peux faire:

// Check if exist cod = 56789
include "database.php";

$querycheck = mysql_query ("SELECT * FROM `YOURTABLE` WHERE `xxx` = '56789';");
$countrows = mysql_num_rows($querycheck);
while($result = mysql_fetch_array($querycheck))
{
    $xxx = $result['xxx'];
    if($xxx == '56789')
    {
      // Exist
    }
    else
    {
      // Not exist
    }
}

Cette méthode est rapide et facile. Pour améliorer la vitesse de la requête dans votre grande table INDEX colonnes 'xxx' (dans mon exemple).

4
Andrea php