在分层查询

时间:2015-10-28 22:41:43

标签: php postgresql recursive-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);

此练习最重要的表格字段是前四个(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)。最后,我希望能够显示孩子,孙子女,父母,曾祖父母等的名字。但是现在我只是想显示后代的数量。例如,如果我导航到MySite / life / mammalia,我可能会看到以下显示:

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

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

Genera: 6
Species: 37

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

1 个答案:

答案 0 :(得分:1)

鉴于表格:

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)

等等......