web-dev-qa-db-fra.com

Comment transférer uniquement les nouvelles valeurs de colonne d'une table à une autre?

J'essaie de créer une nouvelle table: _clients qui sera simplement rempli pour le moment avec le user_id colonne de #__user_usergroup_map.

Idéalement, le code ci-dessous devrait extraire tous les id de #__user_usergroup_map table si correspondant group_id = 10 et uniquement les identifiants qui n'existent pas déjà dans le _clients table.

Cela semble fonctionner lorsque j'ajoute/supprime des utilisateurs de la group_id = 10 dans phpmyadmin. Le $ncl montre une liste correcte.

#__clients mises à jour avec succès lors de l’actualisation de la page, mais j’obtiens une erreur SQL pour l’insertion de la clé primaire en double sur #__clients. Il semble qu'il tente d'ajouter le même utilisateur à #__clients à nouveau (l'utilisateur a été ajouté à la table lors de la première actualisation).

Je pensais que ce code ne devrait sélectionner que $ncl car ces identifiants clients ne sont pas déjà dans le #__clients table. Quelqu'un a des pensées?

$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('a.user_id', 'a.group_id', 'b.id')
      ->from('#__user_usergroup_map as a', '#__clients as b');
$query->join('RIGHT', '#__clients AS b ON a.user_id != b.id')
      ->where('a.group_id = ' . $db->quote("10"));
$db->setQuery($query);
$ncl = $db->loadColumn();
print_r($ncl);

foreach($ncl as $encl) {
    $db    = JFactory::getDbo();
    $query = $db->getQuery(true);
    $query->insert('#__clients')
          ->set('id = ' . (int) $encl);
    $db->setQuery($query);
    $db->execute();
}
3
Michael Brown

Vous pouvez ajuster votre code pour:

$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->select('a.user_id as id') /* changed to load object and insert it */
    ->from('#__user_usergroup_map as a')
    ->leftJoin('#__clients AS b ON a.user_id = b.id') /* you should join with "=" not with "!=" */
    ->where('a.group_id = ' . $db->quote("10"))
    ->where('b.id IS NULL'); /* and filter to "new only" like this */
$ncl = $db->setQuery($query)->loadColumn();
print_r($ncl);

foreach($ncl as $encl) {
    $db->insertObject('#__clients',$encl); /* less code this way */
}

cela fonctionnerait bien avec ce code, mais vous pouvez aller plus loin et le faire

$db = JFactory::getDBO();
$sel_query = $db->getQuery(true);
$ins_query = $db->getQuery(true);
$sel_query->select('a.user_id as id')
    ->from('#__user_usergroup_map as a')
    ->leftJoin('#__clients AS b ON a.user_id = b.id')
    ->where('a.group_id = ' . $db->quote("10"))
    ->where('b.id IS NULL');
$ins_query->insert('#__clients')
    ->columns('id')
    ->values($sel_query);
$db->setQuery(str_replace('VALUES','',$ins_query))->execute();

de cette façon, vous ferez le travail avec une seule demande.

1
Alexandr

Je viens tout juste de réaliser une méthode plus simple ... Comme j'avais déjà créé des tableaux existants, je pouvais simplement utiliser quelque chose comme:

$filteredFoo = array_diff($foo, $bar);

Ce qui filtrera les doublons des deux tableaux représentant chaque table, puis effectuera une insertion SQL avec le $ filtersFoo sans risque de doublons.

1
Michael Brown

Parce que vous avez seulement besoin d'insérer des données de #__user_usergroup_map À #__clientsid est nul ET où group_id Est 10, Vous devez utiliser LEFT JOIN Pour rejoindre les tables. D'autres jointures élimineront les lignes qui produisent une colonne NULL et ce n'est pas souhaitable.

|  #__user_usergroup_map |  LEFT JOIN ON user_id=id  |  #__clients  |
|------------------------|              <            |--------------|
|  user_id  |  group_id  |              <            |      id      |
|-----------|------------|              <            |--------------|
|     1     |     10     |              <            |     NULL     |
|     2     |      9     |              <            |     NULL     |
|     3     |     10     |              <            |     NULL     |
|     4     |      6     |              <            |     NULL     |
|     5     |     10     |              <            |       5      | //previously INSERTED for demonstration's sake
|     6     |     10     |              <            |     NULL     |
|     7     |     10     |              <            |     NULL     |
|     8     |     10     |              <            |     NULL     |
|     9     |      4     |              <            |     NULL     |
|    10     |     10     |              <            |     NULL     |
--------------------------                           ----------------

Avant d’arriver aux solutions, je voudrais attirer l’attention sur le fait que votre syntaxe php/Joomla ne génère pas votre requête.

$query = $db->getQuery(true)
            ->select('a.user_id', 'a.group_id', 'b.id')
            ->from('#__user_usergroup_map as a', '#__clients as b')
            ->join('RIGHT', '#__clients AS b ON a.user_id != b.id')
            ->where('a.group_id = ' . $db->quote("10"));
echo $query->dump();

Génère:

SELECT a.user_id                                    // 2nd & 3rd columns are lost (should have been written as an array, but they were unimportant anyhow)
FROM prefx_user_usergroup_map as a                  // comma joined table is lost (not that it was good for anything)
RIGHT JOIN prefx_clients AS b ON a.user_id != b.id  // this is not the correct join, nor ON logic
WHERE a.group_id = '10'                             // this is half of what is required

Cela retournera zéro lignes.


Bien que je sois confiant que l'extrait de code d'Alexandr fournira le résultat attendu, car la logique/syntaxe est bonne, je vais vous proposer ma version qui présente quelques différences.

$db = JFactory::getDBO();
try {
    $select_query = $db->getQuery(true)
                       ->select("A.user_id")
                       ->from("#__user_usergroup_map A")
                       ->leftJoin("#__clients B ON A.user_id = B.id")
                       ->where("A.group_id = 10 AND B.id IS NULL");
    //echo $select_query->dump();

    $insert_query = $db->getQuery(true)
                       ->insert('#__clients')
                       ->columns('id')
                       ->values($select_query);
    // echo $insert_query->dump();

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

J'ai testé mon extrait de code pour réussir sur mon hôte local. J'ai également créé un Démo SQLFiddle si quelqu'un veut jouer.

  • J'enchaîne chaque requête de la méthode getQuery() pour éliminer les mentions dupliquées de ${Word}_query.
  • Je n'utilise pas d'appels qn() ou q() parce qu'ils ne sont pas nécessaires pour des raisons d'analyse/de sécurité.
  • J'utilise des alias de table en majuscules pour les aider à se distinguer des noms et des colonnes de table.
  • Je ne creuse pas la colonne dans SELECT parce que ce n'est pas obligatoire.
  • setQuery(str_replace('VALUES','',$ins_query)) n'est pas nécessaire.

Si quelqu'un craint que je vole sans vergogne la solution d'Alexandr, je publierai une construction de requête en force brute différente qui aura le même effet si votre colonne #__clients.id Est un PRIMARY/UNIQUE KEY. Je ne pense pas que j'utiliserais cette solution moins élégante (même si, pour une raison quelconque, elle était légèrement plus performante).

$select_query = $db->getQuery(true)
                   ->select("A.user_id")
                   ->from("#__user_usergroup_map A")
                   ->where("A.group_id = 10");
// echo $select_query->dump();

$insert_query = $db->getQuery(true)
                   ->insert('#__clients')
                   ->columns('id')
                   ->values($select_query);

$db->setQuery(str_replace("INSERT", "INSERT IGNORE", $insert_query));
0
mickmackusa