PHP,MySql - 優化
我在一般網站上問過這個,但他們建議我來這裡進行最終優化。它真的歸結為哪個更快;在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。