Mysql

PHP,MySql - 優化

  • March 24, 2016

我在一般網站上問過這個,但他們建議我來這裡進行最終優化。它真的歸結為哪個更快;在php中使用一次訪問的數據庫內容進行大量計算,或者進行兩次數據庫呼叫。(大約有1000-1500個表條目,我必須使用很多。)

這是我原來的問題:

我不是一個出色的 php 編碼器(我來自 C++)。我僅將 php 用於數據庫條目。

我有一個包含以下內容的數據庫:

UserId (an unique int)
AsyncPointsAverage (float)
AsyncPointsAverageRank (a position based on the value immediately above)
AsyncPointsRecentAverage (float an average for the last 5 tests only)
AsyncPointsRecentAverageRank (a position based on the value immediately above)

該表中有大約 1000-1500 個條目。每天早上和下午有 5 人參加一項測試,這會影響他們的總體平均水平和最近的平均水平。(這個是在別處更新的,這裡沒有顯示。)然後計算這5個人,那麼1000-1500的排名都會影響,所以我寫了下面的程式碼。是最優的嗎?

我最關心的是我正在執行大約 1000 次 MySql UPDATE。那很棒嗎?我應該以另一種方式做嗎?(也可以隨意優化函式中的任何其他程式碼。正如我所說,我來自 C++ 背景,所以不太了解 php 的細微差別。)

// Sorts by array entry 1
function ReRankCompareAverage($a, $b)
{
   if($a[1] == $b[1]) return 0;
   else return ($a[1] > $b[1] ? 1 : -1);
}
// Sorts by array entry 2
function ReRankCompareAverageRecent($a, $b)
{
   if($a[2] == $b[2]) return 0;
   else return ($a[2] > $b[2] ? 1 : -1);
}

function ReRank($db)
{
   $i = 0, $j = 0;
   $usersARR = null;

   $stmt = $db->prepare("SELECT UserId, AsyncPointsAverage, AsyncPointsRecentAverage FROM studenttable");
   $stmt->execute();
   if($stmt && isset($stmt) && $stmt->rowCount() > 0)
   {
       $i = 0;
       while(($row = $stmt->fetch(PDO::FETCH_ASSOC)))
       {
           $usersARR[$i][0] = intval($row['UserId']);
           $usersARR[$i][1] = floatval($row['AsyncPointsAverage']);
           $usersARR[$i][2] = floatval($row['AsyncPointsRecentAverage']);
           $i++;
        }
   }
   $stmt->closeCursor(); // mysql_free_result equivalent

   // The first pass of $j == 3 does the ranking by Average, filling position $usersARR[][3] with that rank
   // The second pass of $j == 4 does the ranking by AverageRecent, filling position $usersARR[][4] with that rank
   for($j = 3, $j <= 4; $j++)
   {
       $iCompare = $j == 3 ? 1 : 2;

       usort($usersARR, $j == 3 ? "ReRankCompareAverage" : "ReRankCompareAverageLast");
       $count = count($usersARR);
       if($count > 0)
       {
           // Start it off, with the person with the highest average is rank 1
           $usersARR[$count - 1][$j] = 1; // Position $j is filled with the rank
           // Now loop starting from the second one down
           for($i = $count - 2, $rank = 1; $i >= 0; $i--)
           {
               // Only change the rank if the next one down is strictly lower than the one above, otherwise will share the same rank
               if($usersARR[$i][$iCompare] < $usersARR[$i+1][$iCompare]) $rank = $count - $i; // Otherwise keep the same rank, because they are equal
               $usersARR[$count - 1][$j] = $rank;
           }
       }
    }

    // Now $usersARR is filled with the correct rankings, and they are asscoiated with $UserId
   // Now we must put all of these rankings into the database
   $count = count($usersARR);
   for($i = 0; $i < $count; $i++)
   {
        $stmt = $db->prepare("UPDATE studenttable SET AsyncPointsAverageRank=:AsyncPointsAverageRank, AsyncPointsRecentAverageRank=:AsyncPointsRecentAverageRank "
                       . "WHERE UserId=:UserId");
        $stmt->execute(array(':AsyncPointsAverageRank' => $usersARR[$i][3],
                       ':AsyncPointsRecentAverageRank' => $usersARR[$i][4],
                       ':UserId' => $usersARR[$i][0]));
   }
}

經過幾個答案,包括在數據庫中沒有任何排名,而是通過以下方式即時進行排名:

SET @rank=0; SELECT @rank := @rank +1 AS rank, UserId, AsyncPointsAverage FROM studenttable ORDER BY AsyncPointsAverage DESC

這顯然有一個弊端,即不適合以下情況:如果排名第二和第三的人有88%,那麼他們都將排名第二。下一個人將排在第 4 位(將第 3 位完全排除在外。)

另一個人建議我做類似的事情:

SQL

SELECT
   UserId,
   AsyncPointsAverage,
   AsyncPointsAverageRank
FROM
   studenttable
ORDER BY
   AsyncPointsAverage DESC

PHP

$stmt = $db->prepare("SEE ABOVE...");
$stmt->execute();

if( $stmt && isset( $stmt ) && $stmt->rowCount() ) {
   $rank = 1;
   $last_grade = -1;

   while( ( $row = $stmt->fetch( PDO::FETCH_ASSOC ) ) ) {
       $usersARR[$i][0] = intval($row['UserId']);
       $usersARR[$i][1] = floatval($row['AsyncPointsAverage']);
       $usersARR[$i][2] = floatval($row['AsyncPointsRecentAverage']);

       if( $usersARR[$i][1] < $last_grade ) {
           $rank++;
       }

       $usersARR[$i][3] = $rank;

       $last_grade = $usersARR[$i][1];
   }
}

但顯然這必須為最近的平均值再次完成,即您正在對數據庫進行兩次呼叫。

所以請對我的程式碼超級挑剔。我不是 php 或 mysql 專家,所以我不會以任何方式被冒犯。在我看來,我想學習。

辯論

一旦你更流利地使用 SQL,你就會意識到用 SQL 編寫的許多查詢比用 PHP 編寫的要短得多。由於“程序員時間”很有價值,因此選擇較短的時間通常是最好的。

在 SQL 中做“工作”可能會也可能不會更快。例如,SELECT AVG(x) FROM big_table只返回一行,因此與將整個表複製到 PHP 中相比,網路流量要少得多,因此可能更快。

GROUP BY, ORDER BY, 子查詢等在 SQL 中要簡潔得多。

JOIN在 SQL 中可能總是更快——否則您必須在 PHP 和 MySQL 之間來回切換才能在“第二個”表中進行查找。

數百萬行可能會阻塞 PHP,而 SQL 可以處理幾乎無限數量的行。

但是,另一方面,SQL 必須比 PHP 簡單地遍歷一個數組更努力地獲取一千行。

我該怎麼辦? 無論哪種情況似乎都合適。至於“1000 行”,這對於 MySQL 或 PHP 來說都是微不足道的。我通常在我的程式碼中嵌入計時器(microtime(true));如果網頁看起來很慢,我會尋找其中“最差”的部分進行優化。

你該怎麼辦? 在 SQL 中執行此操作。這是一次學習經歷。僅當 PHP 變得太難時才退回到 PHP。

引用自:https://dba.stackexchange.com/questions/133182