2012-05-15 23 views
5

mam adjacency strukturę trybu lista takiego i chcę, aby policzyć wszystkie tytuł dominującej zgodnie poziomie jak Food = (2,4,3), Fruit = (3,3)Hrabia wynik według poziomu

strukturze drzewa tabel

enter image description here

po które sprawiają, że drzewo tak

enter image description here

przez ten kod IM uzyskanie właściwej tot al jak dla żywności, owoce = 9 = 6

function display_children($parent, $level) 
{ 

$result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"'); 
$count = 0; 
    while ($row = mysql_fetch_array($result)) 
    { 
    $data= str_repeat(' ',$level).$row['title']."\n"; 
    echo $data; 
    $count += 1 + $this->display_children($row['title'], $level+1); 
    } 
    return $count; 
} 

funkcji połączeń

display_children(Food, 0) 

Wynik: 9 // ale chcę, aby uzyskać wynik jak 2,4,3

Ale chcę aby policzyć całkowity wynik takiego dla żywności 2,4,3 i dla owoców 3,3 według poziomu

więc plz poprowadzi jak uzyskać całkowity poziom według

+0

Dlaczego tylko '(2,4,3)' ? Dlaczego '(9,11)' jest pominięty? Potrzebujesz tylko lewej gałęzi tylko? – vyegorov

+0

bcoz Chcę pokazać liczenie według poziomu –

+0

Co masz na myśli przez "liczenie według poziomu?" Proszę, wyjaśnij, jak '(2,4, 3) 'różni się od' (9,11) '? Czy potrzebujesz oddziału o największej głębokości od korzenia może? – vyegorov

Odpowiedz

2

Jeśli chcesz uzyskać kwot według poziomu, a następnie dokonać funkcja zwraca je według poziomu.

function display_children($parent, $level) 
{ 

$result = mysql_query('SELECT title FROM tree WHERE parent="'.$parent.'"'); 
$count = array(0=>0); 
    while ($row = mysql_fetch_array($result)) 
    { 
    $data= str_repeat(' ',$level).$row['title']."\n"; 
    echo $data; 
    $count[0]++; 
    $children= $this->display_children($row['title'], $level+1); 
    $index=1; 
    foreach ($children as $child) 
    { 
    if ($child==0) 
     continue; 
    if (isset($count[$index])) 
     $count[$index] += $child; 
    else  
     $count[$index] = $child; 
    $index++; 
    } 
    } 
    return $count; 
} 

Zauważ, że trudno mi debugować kod, ponieważ nie mam twojego stolika. Jeśli wystąpił jakiś błąd, daj mi znać, a ja to naprawię. Anyways skutkować będzie tablica które powinny zawierać ilości poziomów określonych przez indeksów:

$result=display_children("Food", 0) ; 
var_export($result);//For exact info on all levels 
echo $result[0];//First level, will output 2 
echo $result[1];//Second level, will output 4 
echo $result[2];//Third level, will output 3 

A przy okazji nie jest literówka w swojej bazie danych, id 10 (wołowina) powinien mieć rodzica „mięso” zamiast „Beat " zgaduję.

Jeśli chcesz zobaczyć stronę testową, jej here.

+0

dziękuję za powtórkę, ale daje ona całkowitą wydajność w tablicy jak ta tablica ([] => 9) nie poziom przez –

+0

To naprawdę dziwne, zrobiłem dokładnie tę samą tabelę w mojej bazie danych i działało zgodnie z oczekiwaniami.Być może jest jakaś niedopasowanie wersji php, która sprawia, że ​​wartości tablicy działają źle, spróbuj naprawić kod – Kyborek

+0

ok, próbuję z tym jednym –

3
function display_children($parent, $level) 
{ 

$result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"'); 
$count = ""; 
    while ($row = mysql_fetch_array($result)) 
    { 
    $data= str_repeat(' ',$level).$row['title']."\n"; 
    echo $data; 
    if($count!="") 
     $count .= (1 + $this->display_children($row['title'], $level+1)); 
    else 
     $count = ", ".(1 + $this->display_children($row['title'], $level+1)); 
    } 
    return $count; 
} 

Spróbujmy to raz ..

+0

dzięki za powtórkę, ale kod nie działa, nie daje zliczania –

0

Rozwiązanie przez klasę PHP:

<?php 

class LevelDepCount{ 

    private $level_count=array(); 

    /** 
    * Display all child of an element 
    * @return int Count of element 
    */ 
    public function display_children($parent, $level, $isStarted=true) 
    { 
    if($isStarted) 
      $this->level_count=array(); // Reset for new ask 
    $result = mysql_query('SELECT title FROM tree '.'WHERE parent="'.$parent.'"'); 
    $count = 0; // For the level in the section 
     while ($row = mysql_fetch_array($result)) 
     { 
     $data= str_repeat(' ',$level).$row['title']."\n"; 
     echo $data; 
     $count += 1 + $this->display_children($row['title'], $level+1,false); 
     } 
     if(array_key_exists($level, $this->level_count)) 
      $this->level_count[$level]+=$count; 
     else 
      $this->level_count[$level]=$count; 
      return $count; 
    } 

    /** Return the count by level.*/ 
    public function getCountByLevel(){ 
     return $this->level_count; 
    } 

} 

$counter=new LevelDepCount(); 
$counter->display_children("Food",0); 
var_dump($counter->getCountByLevel()); 

?> 
1

Ten article ma wszystko, czego potrzeba do tworzy drzewo z mysql i jak policzyć pozycję przez poziom

1

Jeśli nie przeszkadza zmiana schematu Mam alternatywne rozwiązanie, które jest znacznie prostsze.

Masz datę w tabeli jak to ...

item    id 
-------------+------ 
Food   | 1 
Fruit  | 1.1 
Meat   | 1.2 
Red Fruit | 1.1.1 
Green Fruit | 1.1.2 
Yellow Fruit | 1.1.3 
Pork   | 1.2.1 

Zapytania są teraz znacznie prostsze, ponieważ są one po prostu proste manipulacje ciąg. Działa to dobrze na małych listach, od kilkuset do kilku tysięcy wpisów - może nie skalować się znakomicie - nie próbowałem tego.

Ale aby policzyć, ile jest rzeczy na drugim poziomie, wystarczy wykonać wyszukiwanie.

select count(*) from items 
where id regexp '^[0-9]+.[0-9]+$' 

Trzeci poziom jest po prostu

select count(*) from items 
where id regexp '^[0-9]+.[0-9]+.[0-9]+$' 

Jeśli chcesz tylko jeden sub-oddział na poziomie 2

select count(*) from items 
where id regexp '^[0-9]+.[0-9]+$' 
and id like "1.%" 

to ma tę zaletę, że nie trzeba biec tak wiele zapytań w bazie danych, a jako bonus znacznie łatwiej jest odczytać dane z tabel i zobaczyć, co się dzieje.

Mam dokuczliwe uczucie, że nie można go uznać za "dobrą formę", ale działa bardzo skutecznie. Byłbym bardzo zainteresowany krytyką tej metody, czy ludzie DB uważają, że to dobre rozwiązanie? Gdyby stół był bardzo duży, robienie skanów i wyrażeń regularnych przez cały czas stałoby się bardzo nieefektywne - twoje podejście lepiej wykorzystywałoby wszystkie indeksy, dlatego mówię, że prawdopodobnie nie skaluje się bardzo dobrze, ale biorąc pod uwagę, że nie • Trzeba prowadzić tak wiele zapytań, może to być warte poświęcenia.

0

Jeśli zmodyfikować zapytanie można uzyskać wszystkie dane w jednym zamachem i bez Tyle obliczeniach (kod niesprawdzone):

/* Get all the data in one swoop and arrange it for easy mangling later */ 
function populate_data() { 
    $result = mysql_query('SELECT parent, COUNT(*) AS amount, GROUP_CONCAT(title) AS children FROM tree GROUP BY parent'); 
    $data = array(); 
    while ($row = mysql_fetch_assoc($result)) { 
     /* Each node has the amount of children and their names */ 
     $data[$row['parent']] = array($row['children'], int($row['amount'])); 
    } 
    return $data; 
} 

/* The function that does the whole work */ 
function get_children_per_level($data, $root) { 
    $current_children = array($root); 
    $next_children = array(); 
    $ret = array(); 

    while(!empty($current_children) && !empty($next_children)) { 
     $count = 0; 
     foreach ($current_children as $node) { 
      $count += $data[$node][0]; /* add the amount */ 
      $next_children = array_merge($next_children, explode($data[$node][1])); /* and its children to the queue */ 
     } 
     ret[] = $count; 
     $current_children = $next_children; 
     $next_children = array(); 
    } 

    return $ret; 
} 

$data = populate_data(); 
get_children_per_level($data, 'Food'); 

To nie powinno być trudne do modyfikacji funkcji, aby wykonać połączenie za wywołanie lub jedno wywołanie na poziom w celu zapełnienia struktury danych bez przenoszenia całej tabeli do pamięci. Zasugerowałbym, że nie, jeśli masz głębokie drzewa z kilkoma dziećmi, ponieważ znacznie wydajniej jest uzyskać wszystkie dane za jednym zamachem i obliczyć je. Jeśli masz płytkie drzewa z dużą ilością dzieci, warto je zmienić.

Byłoby również możliwe połączenie wszystkich elementów w jedną funkcję, ale unikałbym ponownego obliczania danych w przypadku powtarzających się połączeń, gdy nie są one potrzebne. Możliwym rozwiązaniem byłoby uczynienie tej klasy, użycie funkcji populate_data jako konstruktora, który przechowuje ją jako wewnętrzną własność prywatną, i jedną metodę, która jest taka sama jak get_children_per_level bez pierwszego parametru, ponieważ spowodowałaby usunięcie danych wewnętrzna własność prywatna.

W każdym razie proponuję użyć kolumny ID jako odniesienia "rodzica" zamiast innych kolumn. Na początek mój kod ulegnie zerwaniu, jeśli dowolna z nazw zawiera przecinek: P. Poza tym możesz mieć dwa różne elementy o tej samej nazwie. Na przykład możesz mieć Vegetables -> Red -> Pepper, a Red zostanie zgnieciony razem z Owocem Red.

Inną rzeczą, na którą należy zwrócić uwagę, jest to, że mój kod wprowadzi nieskończoną pętlę, jeśli dane DB nie są drzewem. Jeśli na wykresie jest jakikolwiek cykl, to nigdy się nie skończy. Błąd ten można łatwo rozwiązać, zachowując tablicę $visited ze wszystkimi węzłami, które już zostały odwiedzone, i nie przesuwając ich do tablicy $next_children w pętli (prawdopodobnie używając array_diff($data[$node][1], $visited).

Powiązane problemy