计算分层查询中的后代

Count descendants in hierarchical query

有人告诉我,在显示分层数据方面,PostgreSQL 比 MySQL 更好,所以我安装了 PostgreSQL 并准备开始。

这是我标题中的架构(从 pgAdmin 复制):

CREATE TABLE public.gz_life_mammals (
 id integer NOT NULL,
 taxon text NOT NULL,
 parent text NOT NULL,
 parent_id smallint NOT NULL,
 slug text,
 name_common text,
 plural text,
 extinct smallint NOT NULL,
 rank smallint NOT NULL,
 key smallint NOT NULL,
 CONSTRAINT "Primary Key" PRIMARY KEY (id)
);

这是我的数据库连接和第一个查询:

$dbh = pg_connect("host=localhost dbname=geozoo user=postgres");

if (!$dbh) {
die("Error in connection: " . pg_last_error());
}
$sql = "SELECT * FROM gz_life_mammals";

$result = pg_query($dbh, $sql);

while ($row = pg_fetch_array($result)) {
 echo "ID: " . $row[0] . " | ";
 echo "Taxon: " . $row[1] . " | ";
 echo "ParentID: " . $row[3] . "<br>";
}

// free memory
pg_free_result($result);

// close connection
pg_close($dbh);

本练习最重要的 table 字段是前四个字段(idtaxonparentparent_id。数据看起来像这个:

ID | TAXON         | PARENT    | PARENT_ID
1  | Mammalia      | Chordata  | 1
2  | Carnivora     | Mammalia  | 2
3  | Canidae       | Carnivora | 3
4  | Canis         | Canidae   | 4
5  | Canis-lupus   | Canis     | 5
6  | Canis-latrans | Canis     | 5

最后两行代表狼 (Canis lupus) 和土狼 (Canis latrans)。最终,我希望能够显示 children、祖父children、parents、曾祖父parents 等的名字。但现在我只是试图显示后代的数量。例如,如果我导航到 MySite/life/mammalia,我可能会看到以下显示:

Orders: 19
Families: 58
Genera: 688
Species: 8,034

如果我导航到 MySite/life/canidae,它可能会显示如下内容:

Genera: 6
Species: 37

谁能告诉我编写这种查询并显示结果的最佳方法(PHP)?

鉴于 table:

select * from gz_life_mammals;

 id |     taxon     |  parent   | parent_id 
----+---------------+-----------+-----------
  1 | Mammalia      | Chordata  |         1
  2 | Carnivora     | Mammalia  |         2
  3 | Canidae       | Carnivora |         3
  4 | Canis         | Canidae   |         4
  5 | Canis-lupus   | Canis     |         5
  6 | Canis-latrans | Canis     |         5
(6 rows)

和将parent_id翻译成分类名的函数:

create function tax_rank(id integer) returns text as $$
    select case id
             when 1 then 'Classes'
             when 2 then 'Orders'
             when 3 then 'Families'
             when 4 then 'Genera'
             when 5 then 'Species'
           end;
$$ language sql;

您可以使用以下递归查询查询后代数量:

with recursive hier(taxon,parent_id) as (
  select m.taxon, null::integer
  from   gz_life_mammals m
  where  taxon='Mammalia' --<< substitute me
  union all
  select m.taxon, m.parent_id
  from   hier, gz_life_mammals m
  where  m.parent=hier.taxon
)
select tax_rank(parent_id),
       count(*) num_of_desc
from   hier
where  parent_id is not null
group by parent_id
order by parent_id;

 tax_rank | num_of_desc 
----------+-------------
 Orders   |           1
 Families |           1
 Genera   |           1
 Species  |           2
(4 rows)

有趣的部分在里面with recursive。查询的第一部分选择层次结构的根行。第二部分(在 union all 之后)被递归调用,每次都将直接后代添加到先前的结果集中。阅读 this 以详细了解其工作原理。

层级构建好后,可以随意表示。在上面的示例中,仅显示了后代的数量。你也可以得到名字:

with recursive hier(taxon,parent_id) as (
  ...
)
select tax_rank(parent_id),
       taxon as name
from   hier
where  parent_id is not null
order by parent_id;

 tax_rank |     name      
----------+---------------
 Orders   | Carnivora
 Families | Canidae
 Genera   | Canis
 Species  | Canis-lupus
 Species  | Canis-latrans
(5 rows)

一行相同:

with recursive hier(taxon,parent_id) as (
  ...
)
select tax_rank(parent_id),
       string_agg(taxon,', ') as names
from   hier
where  parent_id is not null
group by parent_id
order by parent_id;

 tax_rank |           names            
----------+----------------------------
 Orders   | Carnivora
 Families | Canidae
 Genera   | Canis
 Species  | Canis-lupus, Canis-latrans
(4 rows)

等等...