web-dev-qa-db-fra.com

Comment refactoriser ma séquence de requêtes itérée?

Je construis un composant qui est sur le point d'être finalisé, mais il y a un peu de code que je veux refactoriser. Je pense que ce code peut être complété en une déclaration et j'ai utilisé plus de 3 déclarations.

$db = JFactory::getDBO();    
$query  = $db->getQuery(true);
foreach ($pks as $i => $pk)
{
    //getting the sid from student table
    $query->clear();
    $query->select('sid')
          ->from('#__student')
          ->where('id = '.$pk);    
    $db->setQuery($query); 
    $sid = $db->loadResult(); 
    $query->clear();


    //fetching the data of old entry from log table using sid
    $query->select('*')
          ->from('#__log')
          ->where('sid = '.$sid)
          ->where('pas= 0')
          ->where('stops=0');
    $db->setQuery($query); 
    $obj=$db->loadObject('stdClass');

    //updating the old entry in log table
    $query->clear();
    $query->update('#__log')
          ->set('pas = 1')
          ->where('sid = '.$sid)
          ->where('pas= 0')->where('stops=0');
    $db->setQuery($query); 
    $db->execute();

    //I'm doing this to know about is there at-least one row affected if not then we will not insert a new recorded 
    $count=$db->getAffectedRows();
    //after setting pass to 1 we will ++ class by 1 and year by 1  
    $obj->class=$obj->class+1;
    $obj->year=$obj->year+1;

    if($count == 1)
    {
        $query->clear();
        $query->insert('#__log')
              ->columns('class, year, sid, prmoted')
              ->values($obj->class.','.$obj->year.','.$sid.','. 1);
        $db->setQuery($query);
        $db->execute();
    }       

Dans ce code, je sélectionne d’abord l’enregistrement d’étudiant en cours à partir du #__log table pour la prochaine utilisation. Ensuite, je mets à jour le même enregistrement et mets pass sur 1. Puis, en utilisant les données précédemment sélectionnées, j’insère un nouvel enregistrement dans la même table de journal avec class+1 et year+1 valeurs.

Je pense que ce code peut être hautement refactorisé, est-ce que n'importe quel corps peut m'aider?

1
Sumer Raj Chouhan

Au moins, je vois un moyen de combiner vos deux premières requêtes de sélection en une seule à l’aide de SQL JOIN:

 $query->select('sid')->from('#__student')->where('id = '.$pk);    
$db->setQuery($query); 
$sid = $db->loadResult(); 
$query->clear();


//fetching the data of old entry from log table using sid
$query->select('*')->from('#__log')->where('sid = '.$sid)->where('pas= 0')->where('stops=0');
$db->setQuery($query); 
$obj=$db->loadObject('stdClass');

devient quelque chose comme:

$query
    ->select('l.*')
    ->from('#__log l')
    ->leftJoin('#__student s ON s.sid = l.sid')
    ->where('s.id = ' . (int) $pk)
    ->where('l.pas = 0')
    ->where('l.stops = 0')
$obj = $db->setQuery($query)->loadObject();

(pas testé)

Je ne vois pas un moyen de faire votre SELECT, UPDATE et INSERT en une seule étape.

2
fruppel

Pour tester mon code-refactor suggéré, il me faudrait accéder à vos structures de table et à un nombre suffisant de lignes d'échantillon. Je vais tenter le coup quand même ...

Essentiellement, vous devez exécuter une requête de sélection pour isoler toutes les lignes à traiter. En incluant les valeurs class et year dans votre jeu de résultats éligible, vous pourrez créer un seul lot de mises à jour et un seul lot d'insertions.

Garder le nombre minimal de déplacements dans la base de données est certainement un exemple de meilleure pratique. Mon extrait fera 1 voyage s'il n'y a pas de lignes qualifiantes et 3 voyages s'il y a des lignes qualifiantes. Votre code d'origine cherchait à faire N * 3 voyages vers la base de données. Mon refactor est une amélioration majeure de la performance.

Ce code n'est pas testé (... si quelqu'un remarque une erreur, merci de le commenter):

try {
    // generate result set of qualifying rows which need to be updated and inserted
    $db = JFactory::getDBO();
    $select_query = $db->getQuery(true)
        ->select($db->qn(["a.sid", "a.class", "a.year"]))
        ->from("#__log a")
        ->innerJoin("#__student b ON a.sid = b.sid")
        ->where([
            "a.pas = 0",
            "a.stops = 0",
            "b.id IN (" . implode(',', array_map(function($n){return (int)$n;}, $pks)) . ")"  // just in case $pks is not secure
        ]);
    // don't show rendered queries to the public
    JFactory::getApplication()->enqueueMessage("<div>Rendered Select Query<br><b>" . $select_query->dump() . "</b></div>", 'notice');
    $db->setQuery($select_query);

    if (!$log_rows = $db->loadAssocList()) {
        "No qualifying student logs to process";
    } else {
        $insert_query = $db->getQuery(true)
            ->insert('#__log')
            ->columns($db->qn(["class", "year", "sid", "prmoted"]));

        foreach ($log_rows as $row) {  // generate batched data for just two total queries
            $update_ids[] = (int)$row['sid'];
            $insert_query->values((1 + $row['class']) . ", " . (1 + $row['year']) . ", " . (int)$row['sid'] . ", 1");
        }

        $update_query = $db->getQuery(true)
            ->update("#__log")
            ->set("pas = 1")
            ->where("sid IN (" . implode(",", $update_ids) . ")");

        // don't show rendered queries to the public
        JFactory::getApplication()->enqueueMessage("<div>Rendered Update Query<br><b>" . $update_query->dump() . "</b></div>", 'notice');
        $db->setQuery($update_query);
        $db->execute();
        JFactory::getApplication()->enqueueMessage("<div>" . $db->getAffectedRows() . " Rows Of Data Updated", 'success');

        // don't show rendered queries to the public
        JFactory::getApplication()->enqueueMessage("<div>Rendered Insert Query<br><b>" . $insert_query->dump() . "</b></div>", 'notice');
        $db->setQuery($insert_query);
        $db->execute();
        JFactory::getApplication()->enqueueMessage("<div>" . $db->getAffectedRows() . " Rows Of Data Inserted", 'success');
    }
} catch (Exception $e) {  // this will halt the execution within the try block as soon as something errs
    // don't show actual error message to public
    JFactory::getApplication()->enqueueMessage("Query Syntax Error: " . $e->getMessage(), 'error');
}
0
mickmackusa