Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Optymalizacja funkcji
Forum PHP.pl > Forum > PHP
soska66
Witam

Ponizej przedstawie (o po krotce opisze) pewna funkcje. Powoduje ona przeliczenie punktow zdobywanych przez graczy w cos na wzor ligi typerow.
mamy zatem nastepujace elementy 'ukladanki'

- gracze
- rodzaj ligi
- numer kolejki
- zestawienie meczow
- wyniki wytypowanwe przez graczy
- punkty

  1. <?php
  2. function calculateWeekScore(){
  3.    global $dbh;
  4.    
  5.    $week_id = $_REQUEST["week"]; //id kolejki/tygodnia rozgrywek
  6.    $comp_id = $_REQUEST["comp_id"]; //id rodzaju ligi
  7.    
  8.    $competition = new Competition($comp_id);
  9.    $competition_type = new CompetitionType($competition->comp_type_id);
  10.  
  11. // tworze tablice wszystkich graczy typujacych w danej lidze
  12.  
  13.    $qry = "SELECT sysuser.id as id FROM sysuser, sysuser_competition WHERE sysuser_competition.competition_id = $comp_id AND sysuser_competition.sysuser_id = sysuser.id ORDER BY id ASC";
  14.    $sth = $dbh->prepare($qry);
  15.    $sth->execute();
  16.    
  17.    while ($row = $sth->fetch()) {
  18.        $users[] = $row;
  19.    }
  20.  
  21. // tworze tablice meczow w danej kolejce    
  22.    $qry = "SELECT * FROM fixture WHERE competition_id = $comp_id AND week_id = $week_id ORDER BY id ASC";
  23.    $sth = $dbh->prepare($qry);
  24.    $sth->execute();
  25.        
  26.    while ($row = $sth->fetch()) {
  27.        $fixtures[] = $row;
  28.    }
  29.    
  30.  
  31. //loopuje tablice graczy
  32.  
  33.    foreach ($users as $user){
  34.        $total_score = 0;
  35.        $no_of_predictions = 0;
  36.  
  37. //dla kazdego gracza sprawdzam, czy wytypowal wynik (prediction) na dany mecz kolejki
  38.        foreach ($fixtures as $fixture){
  39.        
  40.  
  41.            $qry = "SELECT * FROM predictions WHERE fixture_id = ".$fixture["id"]." AND user_id = ".$user["id"]."";
  42.            $sth = $dbh->prepare($qry);
  43.            $sth->execute();
  44.                
  45.            $row = $sth->fetch();
  46.            if($sth->rowCount() > 0){
  47.                $no_of_predictions++;
  48.            }
  49.  
  50. // jesli gracz wytypowal wynik, pobieram typ (1, X lub 2) i margines zwyciestwa (jaka iloscia bramek wygrala zwyciezka druzyna)
  51.            $prediction = $row["prediction"];
  52.            $prediction_id = $row["id"];
  53.            $margin = $row["margin"];
  54.                            
  55. //porownuje typy w prawdziwymi wynikami    
  56.                if($fixture["result"] == $prediction){
  57.                                    
  58.                    if($fixture["result"] == "X"){
  59.                        $score = $competition_type->draw_score; //jesli remis - przypisuje ilosc punktow jaka w danym typie ligi przysluguje za celne wytypowanie remisu
  60.                    }elseif($fixture["result"] != ""){
  61.                        $score = $competition_type->winner_score; //jesli zwyciestwo - przypisuje ilosc punktow jaka w danym typie ligi przysluguje za celne wytypowanie zwyciezcy
  62.                        $margin_accuracy = Abs($fixture["margin"] - $margin);
  63.                        $margin_score = getMarginScore($margin_accuracy, $comp_id); // przypisuje punkty za dokladnosc trafienia wyniku (funkcja getMarginScore wysyla zapytanie do bazy - sprawdzana jest jedna tablica w bazie)
  64.  
  65.                        if(!$margin_score){
  66.                            $margin_score = 0;
  67.                        }
  68.                        if(!$score){
  69.                            $score = 0;
  70.                        }
  71.                    }
  72.                    
  73.  
  74.                    $overal_score = $score + $margin_score;
  75.                    $total_score = $total_score + $overal_score;
  76.                    
  77.  
  78.                }else{
  79.                    $overal_score = 0;    
  80.                }
  81.                
  82. // updatuje tablice typow dodajac ilosc punktow jaka w sumie zdobyl gracz za wytypowanie meczu
  83.                $qry = "UPDATE predictions SET score = $overal_score WHERE id = $prediction_id";
  84.                $sth = $dbh->prepare($qry);
  85.                $sth->execute();
  86.                
  87.                $margin_score = 0;
  88.                $score = 0;
  89.                $margin_accuracy = 0;
  90.    
  91.        
  92.        } //koniec petli porownujacej typy z wynikami (tablica fixtures[])
  93.                
  94.        $user_id = $user["id"];
  95.        
  96.        if($no_of_predictions == 0){
  97.            $no_prediction_users .= "$user_id,";
  98.        }else{
  99.        
  100. //jesli kolejka nie jest zakonczona, dodaje do tabeli sysuser_week_score calkowita ilosc punktow zdobytych za wytypowanie wszystkich meczow w danej kolejce.
  101.  
  102.            if(!isCompleted($week_id)){
  103.                $qry = "INSERT INTO sysuser_week_score (sysuser_id, week_id, competition_id, score) VALUES ('$user_id', '$week_id', '$comp_id', '$total_score')";
  104.                $sth = $dbh->prepare($qry);
  105.                $sth->execute();
  106. // jesli jest zakonczona, updatuje istniejacy wpis (mowiac 'zakonczona' mam na mysli, ze admin dokonal juz wczesniej kalkulacji wynikow przynajmniej raz)
  107.            }else{
  108.                $qry = "UPDATE sysuser_week_score SET score = '$total_score' WHERE sysuser_id = $user_id AND competition_id = $comp_id AND week_id = $week_id";
  109.                $sth = $dbh->prepare($qry);
  110.                $sth->execute();
  111.            }
  112.        
  113. // w celu updatowania sumy punktow zebranych  w calej grze, na przestrzeni wszystkich kolejek, pobieram sume punktow zdobytych w tej i wszystkich poprzednich kolejkach. Funkcja getAllWeeksScore wysyla proste zapytanie do jednej tablicy (sysuser_week_score)
  114.  
  115.            $all_weeks_score = getAllWeeksScore($user_id, $comp_id);
  116.            
  117.  
  118. // update tablicy sysuser_score zawierajacej pole z suma punktow zdobytych w calej grze. Tu w zasadzie powinienem zamienic delete i insert na jedno zapytanie update        
  119.            $qry = "DELETE FROM sysuser_score WHERE sysuser_id = $user_id AND competition_id = $comp_id";
  120.            $sth = $dbh->prepare($qry);
  121.            $sth->execute();
  122.            
  123.        
  124.            $qry = "INSERT INTO sysuser_score (sysuser_id, competition_id, score, last_week_score) VALUES ('$user_id', '$comp_id', '$all_weeks_score', '$total_score')";
  125.            $sth = $dbh->prepare($qry);
  126.            $sth->execute();
  127.        
  128.        }
  129.    }
  130.  
  131. // gracze, ktorzy nie dali zadnych typow w danej kolejce, otrzymuja usredniona ilosc punktow innych graczy (to nie moj wymysl, ino klienta :) )
  132.  
  133. //zatem, getAvgWeekScore wykonuje zapytanie na jednej tablicy sumujac punkty poszczegolnych graczy, zdobytych w bierzacej kolejce. Funkcja zwraca ilosc tych punktow podzielona oczywiscie przez ilosc graczy
  134.  
  135.    $avg_score = getAvgWeekScore($comp_id, $week_id);
  136.  
  137. //wszyscy ci gracze, ktorzy nie wzieli udzialu w typowaniu w danym tygodniu, zostali wczesniej wpisani do zmiennej $no_prediction_users (string z id graczy po przecinkach)
  138. // funkcja updateUsersAvgScore 'exploduje' ta zmienna wyluskujac id graczy, a nastepnie odpala foreach dla kazdego gracza. W petli tej odbywa sie dokladnie to samo co mialo miejsce dla graczy, ktorzy typowali... tyle, ze tym razem nie ma porownania typow z wynikami. Odbywa sie zatem dodanie sredniej ilosci punktow do tablicy sysuser_week_score (punktacja w kolejnych kolejkach), zsumowanie punktow z calej gry (getAllWeeksScore) i wpisanie ich do tablicy z punktami (sysuser_score)
  139.  
  140.    updateUsersAvgScore($no_prediction_users, $avg_score, $comp_id, $week_id);
  141.    
  142.  
  143. // zamkniecie kolejki
  144.    $qry = "UPDATE week SET completed = 1 WHERE id = $week_id;";
  145.    $sth = $dbh->prepare($qry);
  146.    $sth->execute();
  147.    
  148.  
  149. }
  150. ?>



Problem polega na tym, ze przy okolo 300 graczach, cala operacja trwa koszmarnie dlugo (2-3 minuty zanim strona zostanie odswiezona). Potrzebuje porady w jaki sposob zoptymalizowac funkcje aby dzialala szybciej. Jesli cos jest nie jasne albo potrzebujecie wiecej informacji, pytajcie...

Dzieki z gory za pomoc
nospor
petla, w tej petli petla, a w tej petli zapytania....

Generujesz mase zapytan. Optymalizacja? Musisz zmniejszyc liczbe zapytan, co sie da pobierac jednym zapytaniem (a sie da) winksmiley.jpg
soska66
Cytat(nospor @ 16.02.2009, 14:39:10 ) *
petla, w tej petli petla, a w tej petli zapytania....

Generujesz mase zapytan. Optymalizacja? Musisz zmniejszyc liczbe zapytan, co sie da pobierac jednym zapytaniem (a sie da) winksmiley.jpg


a mozesz jeszcze przyblizyc? ktore zapytania np... nie prosze o dokladny kod, bo nie jest moim celem pojscie na latwizne i metoda kopiuj wklej z forum smile.gif chociaz w teorii... ktore zapytania moglbym zamienic tudziez polaczyc w jedno

Jak tak patrze to wydaje sie, ze fixtures moglbym wrzucic w jedno zapytanie z predictions (zapytanie z linii 35 bazuje na jednej tylko wartosci z tablicy fixtures). Linia 35 to jedno z dwoch zapytan w petli w petli... to faktycznie da sie uproscic.... ale funkcja getMarginScore() uzaleznia zapytanie od wynikow zapytania poprzedniego (wlasnie linia 35), wiec ona musi tam byc.

Nie bardzo widze co moglbym zrobic z zapytaniami o punkty (pobieranie srednich, inserty itd). One sa wywolywane w ramach jednej petli (foreach users[]) i sa uzaleznione wlasnie od tej petlowanej wartosci user[]
nospor
nie mam czasu na dokladniejszą analize, ale to zapytanie:
$qry = "SELECT * FROM predictions WHERE fixture_id = ".$fixture["id"]." AND user_id = ".$user["id"]."";
nie powinno byc w petli ktora jest w petli. zabijasz baze czyms takim.
Zapewne bez wiekszego problemu powinno sie to uzyskac jednym zapytaniem.
Do wykorzystania:
- podzapytania
i ewentualnie:
- rzeczy grupujace: group by, sum, etc.
soska66
Troche pozmienialem przez co skrocilem dzialanie funkcji do ok 20 sek (300 userow - 300 petli dczytujacych pierwsze zapytanie do bazy)
Zlozylem dwa zapytania z poprzedniego kodu w jedno, dzieki czemu nie wystepuje juz zapytanie w petli ktora jest w jeszcze jednej petli. Zmienilem tez dwa zapytania delete, insert na jedno update.
Funkcja przedstawiona na koncu - updateUsersAvgScore - dodaje uzytkownikom, ktorzy nie brali udzialu w zabawie, usredniony wynik wszystkich userow, ktorzy grali. Dlatego musi ona wystapic na koncu i przefiltrowac ponownie userow (strzelam, ze okolo 10-20% tych 300 userow).

Nie wiem jeszcze co mozna uproscic. 20 sek. przy 300 uzytkownikach (plus ta druga petla z okolo 30-40-oma) to wciaz troche dlugo...

Da sie cos jeszcze z tym zrobic?

  1. <?php
  2. list($comp_margin_scores, $comp_margin_accuracies) = getMarginScore($comp_id); // 1 - getMarginScore wykonuje "SELECT score, margin_accuracy FROM competition_margin WHERE competition_id = $comp_id ";
  3.  
  4.    $qry = "SELECT sysuser.id as id FROM sysuser, sysuser_competition WHERE sysuser_competition.competition_id = $comp_id AND sysuser_competition.sysuser_id = sysuser.id ORDER BY id ASC";
  5.    $sth = $dbh->prepare($qry);
  6.    $sth->execute();
  7.    
  8.    while ($row = $sth->fetch()) {
  9.        $users[] = $row;
  10.    }
  11.  
  12.    $no_prediction_users = "";
  13.    
  14.    
  15.    
  16.    foreach ($users as $user){
  17.        $total_score = 0;
  18.        $no_of_predictions = 0;
  19.        
  20.        $qry = "SELECT fixture.id as fix_id, predictions.prediction as prediction, predictions.id as pred_id, predictions.margin as pred_margin, fixture.result as result, fixture.margin as fix_margin
  21.                    FROM fixture, predictions
  22.                    WHERE fixture.competition_id = $comp_id
  23.                    AND fixture.week_id = $week_id
  24.                    AND fixture.id = predictions.fixture_id
  25.                    AND predictions.user_id = ".$user["id"]."
  26.                    ORDER BY fixture.id ASC";
  27.        
  28.  
  29.        while ($row = $sth->fetch()) {
  30.            $no_of_predictions++;
  31.            $prediction = $row["prediction"];
  32.            $prediction_id = $row["pred_id"];
  33.            $pred_margin = $row["pred_margin"];
  34.            $result = $row["result"];
  35.            $fix_margin = $row["fix_margin"];
  36.            $fix_id = $row["fix_id"];
  37.                
  38.            $margin_score = 0;    
  39.            $score = 0;
  40.            $margin_accuracy = 0;
  41.                
  42.                if($result == $prediction){
  43.                                    
  44.                    if($result == "X"){
  45.                        $score = $competition_type->draw_score;
  46.                    }elseif($result != ""){
  47.                        $score = $competition_type->winner_score;
  48.                        $margin_accuracy = Abs($fix_margin - $pred_margin);
  49.                        
  50.                        $i=0;
  51.                        foreach($comp_margin_accuracies as $comp_margin_accuracy){
  52.                            if($comp_margin_accuracy == $margin_accuracy){
  53.                                $margin_score = $comp_margin_scores[$i];
  54.                            }                    
  55.                            $i++;
  56.                        }
  57.                        
  58.                        if(!$margin_score){
  59.                            $margin_score = 0;
  60.                        }
  61.                        if(!$score){
  62.                            $score = 0;
  63.                        }
  64.                    }
  65.                
  66.                    $overal_score = $score + $margin_score;
  67.                    $total_score = $total_score + $overal_score;
  68.                    
  69.  
  70.                }else{
  71.                    $overal_score = 0;    
  72.                }
  73.                
  74.        
  75.        
  76.        } //koniec while (odczyty z bazy)
  77.  
  78.                $qry = "UPDATE predictions SET score = $overal_score WHERE id = $prediction_id";
  79.                $sth = $dbh->prepare($qry);
  80.                $sth->execute();
  81.                
  82.                $margin_score = 0;
  83.                $score = 0;
  84.                $margin_accuracy = 0;
  85.    
  86.        
  87.        $user_id = $user["id"];
  88.        
  89.        if($no_of_predictions == 0){
  90.            $no_prediction_users .= "$user_id,";
  91.        }else{
  92.        
  93.            if(!isCompleted($week_id)){
  94.                $qry = "INSERT INTO sysuser_week_score (sysuser_id, week_id, competition_id, score) VALUES ('$user_id', '$week_id', '$comp_id', '$total_score')";
  95.                $sth = $dbh->prepare($qry);
  96.                $sth->execute();
  97.            }else{
  98.                $qry = "UPDATE sysuser_week_score SET score = '$total_score' WHERE sysuser_id = $user_id AND competition_id = $comp_id AND week_id = $week_id";
  99.                $sth = $dbh->prepare($qry);
  100.                $sth->execute();
  101.            }
  102.        
  103.  
  104.            
  105.            $qry = "UPDATE sysuser_score SET score = (SELECT sum(score) FROM sysuser_week_score WHERE sysuser_id = $user_id AND competition_id = $comp_id), last_week_score = $total_score WHERE sysuser_id = $user_id AND competition_id = $comp_id";
  106.            $sth = $dbh->prepare($qry);
  107.            $sth->execute();
  108.  
  109.        }
  110.    } // koniec petli foreach users
  111.  
  112.    $avg_score = getAvgWeekScore($comp_id, $week_id); // wykonuje SELECT score FROM sysuser_week_score WHERE week_id = $week_id AND competition_id = $comp_id AND avg = 0
  113.    updateUsersAvgScore($no_prediction_users, $avg_score, $comp_id, $week_id); //kod ponizej
  114.    
  115.    
  116.    $qry = "UPDATE week SET completed = 1 WHERE id = $week_id;";
  117.    $sth = $dbh->prepare($qry);
  118.    $sth->execute();
  119.  
  120.  
  121.  
  122.  
  123.  
  124.  
  125. function updateUsersAvgScore($users, $avg_score, $comp_id, $week_id){
  126.    global $dbh;
  127.    $users = explode(",", $users);
  128.    
  129.    foreach($users as $user_id){
  130.        if($user_id){
  131.            if(!isCompleted($week_id)){
  132.                $qry = "INSERT INTO sysuser_week_score (sysuser_id, week_id, competition_id, score, avg) VALUES ('$user_id', '$week_id', '$comp_id', '$avg_score', 1)";
  133.                $sth = $dbh->prepare($qry);
  134.                $sth->execute();
  135.            }else{
  136.                $qry = "UPDATE sysuser_week_score SET score = '$avg_score', avg = 1 WHERE sysuser_id = $user_id AND competition_id = $comp_id AND week_id = $week_id";
  137.                $sth = $dbh->prepare($qry);
  138.                $sth->execute();
  139.            }
  140.  
  141.            $qry = "UPDATE sysuser_score SET score = (SELECT sum(score) FROM sysuser_week_score WHERE sysuser_id = $user_id AND competition_id = $comp_id), last_week_score = $avg_score WHERE sysuser_id = $user_id AND competition_id = $comp_id";
  142.            $sth = $dbh->prepare($qry);
  143.            $sth->execute();
  144.  
  145.            
  146.        }
  147.        
  148.        
  149.    }
  150.  
  151. }
  152. ?>
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.