web-dev-qa-db-ja.com

反復した一連のクエリをリファクタリングするにはどうすればよいですか?

ファイナライズしようとしているコンポーネントを構築していますが、リファクタリングしたいコードが少しあります。このコードは1つのステートメントで完了することができると思います。3つ以上のステートメントを使用しました。

$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();
    }       

このコードでは、最初に#__logテーブルから現在の生徒のレコードを選択して、次に使用します。次に、同じレコードを更新し、pass1に設定します。次に、以前に選択したデータを使用して、class+1値とyear+1値を含む新しいレコードを同じログテーブルに挿入します。

このコードは高度にリファクタリングできると思います。

1

少なくとも、SQL JOINを使用して、最初の2つの選択クエリを1つに結合する方法がわかりました。

 $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');

次のようになります:

$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();

(未検証)

SELECT、UPDATE、INSERTを1つのステップで行う方法がわかりません。

2
fruppel

私が提案するコードリファクタリングをテストするには、テーブル構造とサンプル行のかなりのチャンクにアクセスする必要があります。とにかく刺します...

基本的に、処理するすべての行を分離するには、1つの選択クエリを実行する必要があります。適格な結果セットにclassyearの値を含めることにより、更新の単一のバッチと挿入の単一のバッチを構築できます。

データベースへの総移動回数を最小限に抑えることは、確かにベストプラクティスの1つの例です。私のスニペットは、条件を満たす行がない場合は1回のトリップを行い、条件を満たす行がある場合は3回のトリップを行います。元のコードは[〜#〜] n [〜#〜] * 3でデータベースにアクセスしようとしていました。私のリファクタリングはメジャーなパフォーマンスの改善です。

このコードはテストされていません(...間違いを見つけた場合はコメントしてください):

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