5

そのような隣接リストモードの構造があり、レベルに応じて親のすべてのタイトルをカウントしたいFood = (2,4,3), Fruit = (3,3)

ツリー テーブル構造

ここに画像の説明を入力

その後、そのようなツリーを作成します

ここに画像の説明を入力

このコードにより、Food = 9、Fruit = 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; 
 }

呼び出し機能

 display_children(Food, 0) 

Result : 9 // しかし、2,4,3 のような結果を得たい

しかし、レベルに応じて、食品2,4,3および果物3,3のようなカウント合計結果を取得したい

レベルに応じて合計を取得する方法を教えてください

4

6 に答える 6

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

これは一度試してみよう..

于 2012-05-16T12:28:47.930 に答える
2

レベルごとに金額を取得する場合は、関数がレベルごとに金額を返すようにします。

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

テーブルがないため、コードをデバッグするのは難しいことに注意してください。エラーがある場合はお知らせください。修正します。とにかく、結果はインデックスで指定された量のレベルを含む配列になります:

$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

ところで、データベースにタイプミスがあります。ID 10 (牛肉) には、「Beat」ではなく「Meat」という親が必要です。

テストページをご覧になりたい方はこちら

于 2012-05-18T07:25:15.077 に答える
1

スキーマを変更してもかまわない場合は、はるかに簡単な代替ソリューションがあります。

あなたはこのようなテーブルにあなたの日付を持っています...

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

クエリは単純な文字列操作であるため、はるかに簡単になりました。これは、数百から数千のエントリの小さいリストで正常に機能します-見事にスケーリングされない可能性があります-私はそれを試していません。

ただし、第2レベルにあるものの数を数えるには、正規表現検索を実行するだけです。

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

3番目のレベルはちょうどです

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

レベル2のサブブランチが1つだけ必要な場合

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

データベースで多くのクエリを実行する必要がないという利点があり、ボーナスとして、テーブル内のデータを読み取って何が起こっているかを確認するのがはるかに簡単です。

これは「良い形」とは言えないかもしれませんが、非常に効果的に機能します。私はこの方法の批評に非常に興味がありますが、DBの人々はこれが良い解決策だと思いますか?テーブルが非常に大きい場合、テーブルスキャンと正規表現を常に実行すると、非常に非効率になります。アプローチでは、任意のインデックスをより有効に活用できます。そのため、これはおそらく十分に拡張できないと思いますが、非常に多くのクエリを実行する必要があるため、取る価値のあるトレードオフになる可能性があります。

于 2012-05-22T01:48:11.073 に答える
1

この記事には、mysql でツリーを作成するために必要なすべてと、アイテムをレベルごとにカウントする方法が記載されています。

于 2012-05-21T17:18:11.773 に答える
0

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());

?>
于 2012-05-19T18:21:49.950 に答える
0

クエリを変更すると、すべてのデータを一度に取得でき、それほど多くの計算を行う必要はありません (コードはテストされていません)。

/* 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');

関数を変更して、呼び出しごとに呼び出しを行うか、レベルごとに 1 つの呼び出しを行い、テーブル全体をメモリに読み込まずにデータ構造を設定することは難しくありません。すべてのデータを一度に取得して計算する方がはるかに効率的であるため、子が数個しかない深いツリーがある場合は、これに反対することをお勧めします。多くの子を持つ浅い木がある場合は、変更する価値があるかもしれません。

すべてを 1 つの関数にまとめることもできますが、必要のないときに繰り返し呼び出しを行うためにデータを再計算することは避けたいと思います。これに対する可能な解決策は、これをクラスにし、それを内部プライベート プロパティとして格納するコンストラクタとして関数を使用し、内部プライベートからデータを取得するため、最初のパラメータがない場合populate_dataと同じ単一のメソッドを使用することです。get_children_per_level財産。

いずれにせよ、他の列ではなく ID 列を「親」参照として使用することをお勧めします。まず、名前のいずれかにコンマが含まれているとコードが壊れます:P. また、同じ名前の 2 つの異なる要素がある場合もあります。たとえば、あなたが持っている可能性がVegetables -> Red -> PepperありRed、果物のRed.

もう 1 つの注意点は、DB データがツリーでない場合、コードが無限ループに入ることです。グラフにサイクルがある場合、それは決して終了しません。このバグは、$visited既にアクセスしたすべてのノードを含む配列を保持し、それらを$next_childrenループ内の配列にプッシュしないことで簡単に解決できます (おそらくarray_diff($data[$node][1], $visited).

于 2012-05-22T04:07:25.893 に答える