層の最大数が既知の階層の場合、結合のカスケードを使用して単一のクエリを実行して、レコード数を見つけることができます。階層の数が 3 つまたは 4 つを超える場合、これはうまくいかないかもしれませんが、うまくいくはずです。
select count(*)
from node_list n1
outer join node_list n2 on n2.parent = n1.nid
outer join node_list n3 on n3.parent = n2.nid
outer join node_list n4 on n4.parent = n3.nid
...など、必要な数のレベルについて。ただし、あまり多くしないようにしてください。そうしないと、パフォーマンスが低下する可能性があります。
現実の世界では、ほとんどの階層システムは実際にはその深さがかなり制限されています。理論的には無制限であっても。たとえば、サイト メニューでは構造のレベルを無制限にすることができますが、3 つまたは 4 つを超えると使いにくくなります。ネストに制限を課すかどうかはあなた次第ですが、それにより作業が簡単になる場合があります。
ただし、どこまで深くなるかわからない無限の階層がある場合、または上記のクエリが遅すぎる場合は、ループが必要になります。そのループが MySQL ストアド プロシージャにあるか、PHP にあるかは重要ではありません。いずれかの方法でループが必要になります。for
ただし、心配しているループの混乱である必要はありません。
私は再帰的なPHP関数でそれを行います。多分このようなもの:
function countDescendants($db, $nid) {
$total = 0;
$query = "select nid from Nodes where parent = ".(int)$nid;
$res = $db->query($query);
foreach($res as $data) {
$total += countDescendants($db, $data['nid']);
}
$total += $res->num_rows;
return $total;
}
次に、それを呼び出して、1 行のコードで答えを得ることができます。
$number_of_descendants = countDescendants($starting_nid);
mysqli
かなり単純な再帰関数 ( DB に使用していて、関数に渡すために接続が既にソートされていると仮定しました)。
確かに、非常に巨大な階層がある場合や、何度もクエリを実行している場合は、少し遅くなる可能性がありますが、私が示したこの基本的な例を改善することで速度を上げる方法があります。たとえば、準備済みステートメント クエリを使用して、同じステートメントに異なる nid 値を入力するだけで済みます。これにより、DB 作業の大部分を節約できます。しかし、小さな階層で単純に使用する場合は、上記のコードで問題ありません。
これらの手法の 1 つの大きな落とし穴は、ノード構造にループがある場合です。つまり、親 ID として独自の子孫の 1 つを持つノードです。このシナリオでは、上記の PHP コードで無限ループが発生し、ネストされた結合 SQL クエリの場合にレコード数がひどく偏る原因になります。どちらの場合でも、システムでこのような状況が発生する可能性がある場合は、それに対するコードを作成する必要があります。しかし、それは物事を複雑にするので、ここでは触れません。
それが役立つことを願っています。
(注:上記のコードはテストされていません:実行せずに回答に直接入力しました。タイプミスがある場合はお詫びします)