web-dev-qa-db-fra.com

Insérer une requête en ignorant les doublons (sans clé)

J'ai une question:

$query_add_analogue
  ->insert($db->quoteName('#__parts_analogues'))
  ->columns($db->quoteName($columns))
  ->values($value);
$db->setQuery($query_add_analogue);
$db->execute();

La table "#_parts_analogues" a deux colonnes - "original_id" et "analogue_id".

J'ai besoin d'ignorer la fonction d'insertion, lorsque la paire de valeurs existe déjà dans la table. Il n'y a pas de clé primaire dans la table.

1
user3774771

Lorsque vous utilisez MySQL, vous pouvez créer un index unique sur vos colonnes original_id et analogue_id.

Ensuite, utilisez une requête brute telle que:

$db = JFactory::getDbo();
$query = 'INSERT IGNORE INTO ' . $db->quoteName('#__parts_analogues') VALUES ("your", "values");
$db->setQuery($query)->execute();

Cela fonctionne comme un insert, mais ne renvoie pas d'erreur.

N'oubliez pas que travailler sans index peut entraîner de graves problèmes de performances.

3
christian

Option n ° 1 - la voie propre (mon approche recommandée)

Appelez cette requête de phpMyAdmin pour établir de manière permanente les deux colonnes en tant que UNIQUE KEYS. (Vous devrez remplacer le préfixe #_ Par votre chaîne de préfixe privé.)

ALTER TABLE `#__parts_analogues` ADD UNIQUE KEY(original_id, analogue_id)

Ensuite, chaque fois que vous souhaitez jeter de nouvelles lignes dans la table, vous pouvez utiliser une requête INSERT IGNORE INTO Pour éviter de recevoir ces erreurs de syntaxe. Ce réglage est appliqué à l’appel setQuery().

Voici comment j'écrirais le code OO:

$db = JFactory::getDBO();
try {
    $insert_query = $db->getQuery(true)
                       ->insert("#__parts_analogues")
                       ->columns("original_id, analogue_id")
                       ->values($value); // I'm not sure if you are delivering a string, 1-dim array, or multi-dim array

    $db->setQuery(str_replace("INSERT", "INSERT IGNORE", $insert_query));    // add IGNORE 
    $db->execute();
    echo $db->getAffectedRows() , " row(s) inserted into parts_analogues table";
} catch (Exception $e) {
    echo  "Syntax Error"; // . " & Error: " . $e->getMessage();
}

Je ne sais pas si $value Est une chaîne séparée par des virgules, un tableau unidimensionnel contenant deux éléments ou un tableau multidimensionnel contenant des ensembles de sous-tableaux à 2 éléments. Si ces valeurs id sont des entiers et proviennent d'une source externe, vous devez convertir chaque id en tant que (int) Avant d'alimenter la requête pour des raisons de sécurité. Voici une littérature pertinente:

Je devrais également signaler qu’il existe techniquement une possibilité de procéder à plusieurs remplacements non voulus dans la requête en utilisant str_replace(), ayez votre chapeau de feuille d'étain, vous pouvez l'utiliser avec le même effet:

$db->setQuery(preg_replace('~INSERT \K~', 'IGNORE ', $insert_query, 1)));    // add IGNORE
// Joomla puts \r\n (or just \n depending on your operating system) at the start of the query; var_dump($db) after setQuery() to see.

Option n ° 2 - quelle OMI est la méthode la moins élégante, car elle nécessite deux allers-retours vers la base de données.

Fondamentalement, vous exécuterez une requête SELECT COUNT (*) avec une clause WHERE qui recherche des correspondances à l’aide de vos données $values Afin de déterminer si la paire de valeurs existe dans la table, vous appelez alors une requête INSERT quand elle est unique. * Remarque, vous pouvez utiliser deux appels de méthode where(), mais je ne préfère pas ce style, car il s'écarte trop de la syntaxe raw SQL. Vous pouvez également coder en dur le AND et écrire une seule chaîne, mais j'essayais d'obtenir que le bloc de code entier corresponde à la largeur disponible sur cette page.

$db = JFactory::getDBO();
try {
    $select_query = $db->getQuery(true)
                       ->select("COUNT(*)")
                       ->from("#__parts_analogues")
                       ->where(
                           array(
                             "original_id = " . (int)$original_id),
                             "analogue_id = " . (int)$analogue_id)
                           )
                         )
                       ->setLimit(1);

    $db->setQuery($select_query);
    if (!$db->loadResult()) {
        // if count is 0 (false-y) that means the values are unique
        $insert_query = $db->getQuery(true)
                           ->insert("#__parts_analogues")
                           ->columns("original_id, analogue_id")
                           ->values((int)$original_id . "," . (int)$analogue_id);

        $db->setQuery($insert_query);
        $db->execute();
        echo "Row Added";
    } else {
        echo "Non-unique Values";
    }
} catch (Exception $e) {
    echo  "Syntax Error"; // . " & Error: " . $e->getMessage();
}
2
mickmackusa

Je donne une solution à ce problème uniquement aux nouveaux lecteurs qui peuvent rechercher une solution à ce type de problèmes. Vous ne pouvez pas vraiment résoudre ce problème avec les méthodes de requête Joomla. Comme indiqué ci-dessus, une simple requête MySQL telle que "INSERT IGNORE INTO" devrait fonctionner, mais ce n'est pas toujours la bonne solution. Au lieu de cela, la requête MySql "INSERT INTO ... ON DUPLICATE KEY UPDATE" fonctionne. Pourquoi? Parce que si un enregistrement existe déjà, il sera simplement mis à jour (probablement avec le même enregistrement ou avec un enregistrement mis à jour avec les mêmes identifiants et champs uniques, pour que ce ne soit pas un problème), si l'enregistrement n'existe pas, alors il être créé, inséré comme un nouvel enregistrement dans la table.

Tout d’abord, pour comprendre ce problème particulier dans la question initiale (dans ce problème, vous souhaitez insérer ou mettre à jour une paire de clés dans la table), vous ne devez pas rendre les deux colonnes de la table UNIQUE - "original_id" et "analogue_id". Parce que si vous les rendez uniques, vous ne pourrez pas réinsérer une paire de nouvelles clés dont l’une est identique à la clé précédemment insérée. Par exemple, vous avez précédemment inséré une paire de clés comme original_id = 1 et analogue_id = 1, et vous essayez maintenant d’insérer une nouvelle paire comme original_id = 1 et analogue_id = 2. Si ces clés sont UNIQUES dans la table, vous ne pourrez pour insérer la deuxième paire en tant que nouvelle paire et en même temps, vous ne voudrez pas non plus les mettre à jour.

Donc, ce que je ferais, c’est que je générerais (dans mon code) une vraie clé UNIQUE à partir des clés original_id et analogue_id et que je les insérerais avec la clé Unique générée dans la table. Et ensuite, lorsque je mets à jour la table avec de nouvelles données ou que j'insère de nouvelles données dans la table, j'utiliserais par exemple:

$my_unique_key = $original_id . $analogue_id . "something_to_make_it_more_unique";

$myTableItems = new stdClass();

// You can put all of the data items into the $myTableItems object
// if you have many of these data
// I will not do that here due to time saving

// and in foreach loop

foreach ($myTableItems as $item) {

  $fields = array($db->quoteName('myunique_key') . ' = ' . $db->quote($item->my_unique_key),
            $db->quoteName('original_id') . ' = ' . $db->quote($item->original_id),
            $db->quoteName('analogue_id') . ' = ' . $db->quote($item->analogue_id));

  $values = array($item->my_unique_key, $item->original_id, $item->analogue_id);

  $query = "INSERT INTO #__mytable (myunique_key, original_id, analogue_id) VALUES ('" . implode("', '", $values) . "')
  ON DUPLICATE KEY UPDATE" . implode(", ", $fields);

  $db->setQuery($query)->execute();
  }

Et si vous souhaitez simplement insérer les nouveaux enregistrements et ne souhaitez pas mettre à jour les anciens enregistrements, vous pouvez utiliser la requête INSERT IGNORE INTO comme ceci:

$query = "INSERT IGNORE INTO #__mytable (myunique_key, original_id, analogue_id) VALUES ('" . implode("', '", $values) . "')";

La référence simple sur ces requêtes SQL est par exemple ici .

1
Zollie