2016-03-23 21 views
5

Tôi không phải là một coder php tuyệt vời (tôi đến từ C++). Tôi đang sử dụng php chỉ cho mục nhập cơ sở dữ liệu.PhP, MySql - Tối ưu hóa Mã số

Tôi có một cơ sở dữ liệu như sau:

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) 

Có khoảng 1.000-1.500 mục trong bảng đó. Mỗi buổi sáng và buổi chiều, 5 người đều làm bài kiểm tra có ảnh hưởng trung bình tổng thể và trung bình gần đây của họ. (Điều này được cập nhật ở nơi khác, nhưng không được hiển thị ở đây.) Sau đó được tính cho 5 người đó, sau đó thứ hạng của tất cả 1000-1500 sẽ được thực hiện, vì vậy tôi đã viết mã bên dưới. Nó có tối ưu không?

Điều tôi quan tâm nhất là tôi đang thực hiện cập nhật MySql khoảng 1000 lần. Điều đó có tuyệt không? Tôi có nên làm theo cách khác không? (Cũng cảm thấy tự do để tối ưu hóa các mã khác trong hàm. Như tôi đã nói, tôi từ một C++ nền, do đó, không thực sự biết các sắc thái của 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])); 
    } 
} 
+0

Bạn có thể sử dụng giao dịch và thực hiện tất cả các cập nhật. Tôi không chắc chắn nếu MyISAM hỗ trợ giao dịch nhưng InnoDb thì có. – frz3993

+0

Mã của bạn được tiêm an toàn và chạy một nghìn truy vấn cập nhật nhỏ không có vấn đề gì đối với bất kỳ máy chủ cơ sở dữ liệu hiện đại nào. Tôi muốn nói bạn ổn. Nếu bạn muốn tối ưu hóa thêm, bạn đang ở trên trang web StackExchange sai. :) –

+0

Không nhìn vào chi tiết cho vấn đề của bạn vì cách bạn làm nó có vẻ ổn nhưng chỉ vì "mục đích thảo luận", nếu bạn muốn tránh hàng nghìn bản cập nhật trong db của mình, bạn có thể xem xét một thứ hạng khác "hệ thống, giống như một cột trong bảng của bạn đề cập đến" trước đó "hoặc" yếu tố tiếp theo ". Bằng cách này, cập nhật xếp hạng của bạn sẽ chỉ ảnh hưởng đến các mục "được phân loại lại" và hàng xóm ... – Julo0sS

Trả lời

4

Làm thế nào để bạn cần phải sử dụng các bảng xếp hạng ? Có lẽ bạn lưu trữ Ranks không cần thiết? Họ có thể dễ dàng tính toán:

SELECT COUNT(*) 
FROM studenttable 
WHERE AsyncPointsAverage > $currentUserVariableAsyncPoints 

Để hiển thị TOP 10:

SELECT * FROM studenttable ORDER BY AsyncPointsAverage DESC LIMIT 0,10 

, vv

EDIT:

Để hiển thị bảng xếp hạng hoàn chỉnh với số vị trí hoặc là bạn có thể làm điều đó trong PHP (bạn đã có nó - vòng lặp bên trong nơi bạn tìm nạp hàng chỉ hiển thị biến số $i++). Hoặc bạn có thể thử với SQL thuần túy (cá nhân tôi thích nó hơn):

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

Các sinh viên sẽ có thể đăng nhập vào hệ thống và xem thứ hạng của họ, bằng cách thu thập thông qua các trang (để xem cách họ so sánh với mọi người khác) . Họ cũng có thể đăng nhập vào trang cá nhân của riêng họ mà sẽ có thứ hạng tổng thể và xếp hạng gần đây của họ.Tôi nghĩ tính toán các bảng xếp hạng một lần sau mỗi bài kiểm tra sẽ là cách tốt nhất để làm điều đó, thay vì tính toán nó mỗi lần cho hàng ngàn lượt xem của mỗi học sinh mỗi ngày. (Chỉ có 2 bài kiểm tra mỗi ngày chỉ cho 5 học sinh mỗi bài kiểm tra.) – Rewind

+0

Tôi đã chỉnh sửa câu trả lời của mình. – Mark

+0

Phương pháp xếp hạng của bạn có cùng thứ hạng với mức trung bình bằng nhau không? Ví dụ: học sinh lớp 2 và 3 có trung bình 88%. Cả hai đều sẽ được xếp hạng 2. Sau đó, người tiếp theo sẽ được xếp hạng thứ 4 (tức là rời khỏi cấp bậc thứ 3 hoàn toàn, bởi vì 2 người có thứ hai.) – Rewind

1

Chỉ cần mở rộng câu trả lời của Mark, bạn không cần phải tính toán lại xếp hạng mỗi khi bạn thêm kết quả kiểm tra. Nó chắc chắn là chức năng nhưng nó không phải là tối ưu. Cách tốt nhất là tính toán thứ hạng khi bạn hiển thị nó. Nếu bạn muốn cho phép sinh viên có kết quả tương tự và cùng cấp bậc, bạn luôn có thể tính toán thứ hạng trong PHP.

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]; 
    } 
} 

Bạn chỉ cần thay đổi các lĩnh vực đọc và ORDER BY lĩnh vực nếu bạn muốn đặt hàng bằng trung bình gần đây để thay thế.