Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Jak zoptymalizować tą pętlę i jej rekurencję
Forum PHP.pl > Forum > PHP
jajcarzd1
Witam

Mam w bazie danych zapisane kategorie gdzie tabela ta posiada między innymi takie nagłówki jak idCategories,idParent,name itp.

Potrzebuję wyciągnąć wszystkie kategorie wraz z ich potomkami niezależnie od stopnia zagnieżdzenia aby potem móc zrobić sobie menu np. takie http://jquery.bassistance.de/treeview/demo/?1 czyli wszystkie elementy pociągnięte od razu a nie w trakcie kliknięcia na danego potomka. Wiec konstrukcje <ul> z zagłębieniami

W tabeli kategorii jest około 1400 kategorii na chwilę obecną około 14 pierwszego poziomu czyli z idparent = 0, 165 drugiego i jakieś 1300 trzeciego poziomu. Elementy pobieram rekurencyjnie ale to nie jest za bardzo problemem a ilość pętli jaka musi być wykonana i jest ich w sumie w chwili obecnej około 240 000, czas wykonywania tej operacji to około 0,4 sekundy i tak nie mam za bardzo pomysłu co tu można jeszcze poprawić



  1. /* tu pobieram wszystkie elementy z idparent = 0 i tworzę tablicę obiektów
  2.   bo normalnie to mi zwraca tablicę dwuwymiarową. Na każdym poziomie tworzę obiekty
  3.   gdyż nie bardzo widziałem rozwiązanie aby kombinować z określaniem kolejnego poziomu zagłębienia tablicy
  4.   a tak przekazuję referencję obiektu */
  5.  
  6. $result = $this->classMysql->ta($sql);
  7. $r = array();
  8.  
  9. foreach ($result as $item) {
  10. $object = new stdClass();
  11. $object->idCategories = $item['idCategories'];
  12. $object->item = $item['name'];
  13. $object->nodes = array();
  14. $r[] = $object;
  15. }
  16.  
  17. $this->dbGetNodes($r);
  18.  
  19.  
  20. private function dbGetNodes(&$result) {
  21.  
  22. $this->allCategories = $this->classMysql->ta("SELECT idCategories,idParent,name FROM categories ORDER BY sequence");
  23. $allIdParents = array();
  24. $this->allIdParentsAsKeys = array();
  25.  
  26. foreach ($this->allCategories as $cat) {
  27. $allIdParents[] = $cat['idParent'];
  28. }
  29.  
  30. $allIdParents = array_unique($allIdParents);
  31.  
  32. foreach ($allIdParents as $item) {
  33. $this->allIdParentsAsKeys[$item] = 1;
  34. }
  35.  
  36. for($i = 0, $ii = count($result); $i<$ii; $i++) {
  37.  
  38. $this->createNodes($result[$i]->idCategories,$result[$i]);
  39.  
  40. }
  41.  
  42.  
  43. }
  44.  
  45.  
  46. private function createNodes($idCategories,$object) {
  47.  
  48. $this->iteration++;
  49.  
  50.  
  51. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {
  52.  
  53. $this->iteration2++;
  54.  
  55. if($this->allCategories[$i]['idParent'] == $idCategories) {
  56.  
  57. $ob = new stdClass();
  58. $ob->idCategories = $this->allCategories[$i]['idCategories'];
  59. $ob->item = $this->allCategories[$i]['name'];
  60. $ob->nodes = array();
  61.  
  62. $object->nodes[] = $ob;
  63.  
  64. /*
  65. if(in_array($allCategories[$i]['idCategories'],$allIdParents)) {
  66. $this->createNodes($allCategories[$i]['idCategories'],$ob,$allCategories,$allIdParents);
  67. }
  68. */
  69.  
  70. /* szybsze niż in_array */
  71.  
  72. if(isset($this->allIdParentsAsKeys[$this->allCategories[$i]['idCategories']])) {
  73. $this->createNodes($this->allCategories[$i]['idCategories'],$ob);
  74. }
  75.  
  76.  
  77. }
  78.  
  79.  
  80. }
  81.  
  82. }


Jakby ktoś miał jakis pomysł jak to zoptymalizować to byłbym wdzięczny.
Pozdrawiam
wookieb
Zastosować inny rodzaj drzewa w bazie danych. Left right, bądź IP
jajcarzd1
Cytat(wookieb @ 19.05.2010, 15:43:33 ) *
Zastosować inny rodzaj drzewa w bazie danych. Left right, bądź IP


Zmiany w bazie odpadają dlatego pytam o optymalizację tego co jest.
wookieb
Jak już to:
1) Cache - dla każdego rodzica listę ich dzieci
2) Dlaczego pobierasz WSZYSTKIE elementy a dopiero potem je "wyszukujesz" w duzej tablicy?
Załóż klucz "index" na pole "idParent" w swojej bazie i wyszukuj tylko te rekordy, które potrzebujesz
jajcarzd1
Cytat(wookieb @ 19.05.2010, 15:57:35 ) *
2) Dlaczego pobierasz WSZYSTKIE elementy a dopiero potem je "wyszukujesz" w duzej tablicy?
Załóż klucz "index" na pole "idParent" w swojej bazie i wyszukuj tylko te rekordy, które potrzebujesz


Po to pobieram wszystkie i przelatuję w pętli aby ograniczać ilośc zapytań do bazy (których przy założeniu że kategorie trzeciego poziomu już nie mają potomków) będzie 165. Ta sama wersja ale oparta na pobieranie kategorii z bazy za kazdym razem wykonuję mi sie prawie 1,5 sekundy więc to tez odpada.
wookieb
A stworzyłeś klucze tak jak mówiłem?
Pozostaje Ci cache jak nic innego nie chcesz robić.
taktu
Można trochę udoskonalić kod, wywalić foreach czy wyciągnąć count przed pętle - to tak na pierwszy rzut oka. Może tutaj znajdziesz coś co pomoże.
jajcarzd1
Cytat(wookieb @ 20.05.2010, 09:48:10 ) *
A stworzyłeś klucze tak jak mówiłem?


No pewnie ale tak jak napisałem za dużo zapytań by było.


Cytat(taktu @ 20.05.2010, 11:46:46 ) *
Można trochę udoskonalić kod, wywalić foreach czy wyciągnąć count przed pętle - to tak na pierwszy rzut oka. Może tutaj znajdziesz coś co pomoże.


No a co mi da wywalenie pętli jak ja jej potrzebuję. Count jest odpalany raz na początku pętli a nie za każdą iteracją. Natomiast zastosowanie tylko jednego count-a i przypisanie go do właściwości klasy aby uniknąć ponownego odpalania w rekurencji nie poprawiło efektywności. Zerknę jeszcze na link który zapodałeś
wookieb
No to nie ma opcji. Przebudowa drzewa albo siedzisz dodatkowych pare godzin nad cache.
croc
Cytat(jajcarzd1 @ 20.05.2010, 12:02:06 ) *
Count jest odpalany raz na początku pętli a nie za każdą iteracją.

Niestety mylisz się. Jak masz count( ) w środkowej części for to odpala się co iterację i tak musi być, bo przecież mógłbyś sobie dodawać/usuwać w pętli elementy tablicy i wtedy cache'owanie tej wartości byłoby bez sensu.
jajcarzd1
Cytat(croc @ 20.05.2010, 12:38:17 ) *
Niestety mylisz się. Jak masz count( ) w środkowej części for to odpala się co iterację i tak musi być, bo przecież mógłbyś sobie dodawać/usuwać w pętli elementy tablicy i wtedy cache'owanie tej wartości byłoby bez sensu.


Czegoś chyba nie kumam mówisz o tej części ?

  1.  
  2. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {
  3.  


Przecież tu mam odpalanie counta raz dla danej rekurencji. Przecież gdyby było co iterację to by było

  1.  
  2. for($i = 0, $ii < count($this->allCategories); $i++) {
  3.  
marcio
@cron ma racje wywal tego count() nad petle, przypisz to co zwraca do jakiejs zmiennej i to ja przekazuj do petli.
jajcarzd1
Cytat(marcio @ 20.05.2010, 13:06:26 ) *
@cron ma racje wywal tego count() nad petle, przypisz to co zwraca do jakiejs zmiennej i to ja przekazuj do petli.


Napisałem wcześniej że nie widziałem praktycznie żadnej zmiany wydajności na plus gdy dałem wynik count-a do właściwości klasy

  1. for($i = 0, $i < $this->quantity; $i++)
phpion
@jajcarzd1:
Nie słuchaj ~crona ani ~marcio odnośnie tej pętli. Pierwotnie, czyli tak:
  1. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {

miałeś dobrze.
jajcarzd1
Cytat(phpion @ 20.05.2010, 13:21:31 ) *
@jajcarzd1:
Nie słuchaj ~crona ani ~marcio odnośnie tej pętli. Pierwotnie, czyli tak:
  1. for($i = 0, $ii = count($this->allCategories); $i<$ii; $i++) {

miałeś dobrze.



No do tego rozwiązania jestem przekonany że miałem dobrze, ale oczywiście w każdej rekurencji pętla leci od nowa więc tam jest ponownie odpalany count(), ale tak jak napisałem przerzucenie jego wyniku do właściwości obiektu praktycznie nic nie dało bo to zbyt mały zysk.
taktu
z wywaleniem foreach miałem na myśli zamianę na for/while ale wiele to nie da bo tam akurat masz mało iteracji.
croc
Aha, zawarłeś to jednak w pierwszej części for, no to ok - liczy raz. Nie wczytałem się dokładnie w kod, sorry smile.gif Ale uważam, że takie upychanie na siłę kodu nie jest zbyt dobrym pomysłem. Wprawdzie masz argument, że tego counta liczysz tylko na chwilę, dla pętli, ale chyba lepiej jak widać od razu poszczególne części for.
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.