对于具有已知最大层数的层次结构,您可以使用级联连接执行单个查询以查找记录计数。如果层数大于三层或四层,这可能不漂亮,但应该可以。
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
...依此类推,您可以根据需要设置多个级别。尽量不要太多,否则性能可能会受到影响。
在现实世界中,大多数等级系统的深度实际上是相当有限的。即使它们在理论上是无限的。例如,站点菜单可能允许无限级别的结构,但超过三到四个就很难使用。是否对嵌套施加限制取决于您,但这可能会使事情变得更容易。
但是,如果您确实有一个开放式层次结构,您不知道它可以走多远,或者如果上述查询太慢,那么您将需要一个循环。该循环是在 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;
}
然后你可以调用它并用一行代码得到你的答案:
$number_of_descendants = countDescendants($starting_nid);
一个相当简单的递归函数(我假设您正在使用mysqli
您的数据库,并且您的连接已经排序以传递给函数)。
当然,如果你有一个非常大的层次结构,或者你要多次查询它,它可能会有点慢,但有一些方法可以通过改进我给出的这个基本示例来加速它。例如,您可以使用准备好的语句查询,并使用不同的 nid 值填充相同的语句:这将节省大部分数据库工作。但是对于小层次结构的简单使用,上面的代码应该没问题。
使用这些技术中的任何一个的一个大缺陷是,如果您的节点结构中有一个循环——即,一个节点具有它自己的一个后代作为它的父 ID。这种情况会导致上述 PHP 代码出现无限循环,并且在嵌套连接 SQL 查询的情况下会导致记录计数严重倾斜。无论哪种情况,如果您的系统可能出现这种情况,您将需要针对它编写代码。但这确实使事情复杂化,所以我不会在这里讨论它。
希望有帮助。
(注意:上面的代码未经测试:我直接将其输入答案而没有运行它;如果有任何错别字,我们深表歉意)