使用PostgreSQL选择层次结构的某些级别

时间:2014-04-20 21:30:04

标签: sql regex postgresql

我有一个表使用枚举路径表示层次结构的表:

  id | name  | path  
-----+-------+-------
   1 | Bob   | 1
   2 | Joe   | 2
   3 | Kyle  | 2/3
   4 | Sarah | 2/4
   5 | Jim   | 5
   6 | Steve | 5/6
   7 | Adam  | 5/7
   8 | Frank | 5/7/8
   9 | Sue   | 5/7/9

我需要一个查询,它返回给定记录的直接子项,并为每个子项返回其下所有子记录的计数。

例如,针对Jim(id = 5)的查询应返回以下集合:

  id | name  | path  | subrecords
-----+-------+-------+------------
   6 | Steve | 5/6   | 0
   7 | Adam  | 5/7   | 2

尝试:

如果我这样做:

select did, name, path, SUBSTRING(path FROM '5\/[^\/]*$') as child_path from items where path ~ '5\/.*';

我参与其中的一部分...

 did | name  | path  | child_path 
-----+-------+-------+---------
   6 | Steve | 5/6   | 5/6
   7 | Adam  | 5/7   | 5/7
   8 | Frank | 5/7/8 | 
   9 | Sue   | 5/7/9 | 

...但是记录8和9需要汇总为7以下的计数。

我试过了:

select SUBSTRING(path FROM '5\/[^\/]*$') as child_path, COUNT(id) as count from items where path ~ '5\/.*' GROUP BY child_path;

哪个让我:

child_path | count 
----------+-------
          |     2
 5/7      |     1
 5/6      |     1

不。

我怎么能实现这个目标?

2 个答案:

答案 0 :(得分:2)

在我看来,这样做了。

SELECT i.id,
       i.name,
       i.path,
       count(ii.id) AS cnt
FROM items i
LEFT OUTER JOIN items ii
ON ii.id != i.id AND ii.path LIKE i.path || '%'
WHERE i.path ~ '5\/[^\/]*$'
GROUP BY i.id,
         i.name,
         i.path;

http://sqlfiddle.com/#!15/b5b4c/23

答案 1 :(得分:2)

我认为你可以在没有自我加入的情况下做到这一点。

您可以使用以下表达式选择给定节点的所有后代:path like '5' || '/' || id || '%'。然后,您可以通过选择最多5个子字符串和下一个id:

来聚合它们
select left(path, length('5'||'/') + position('/' in split_part(path||'/', '5'||'/', 2)) - 1) as child,
       count(*) - 1 as numdescendants
from items
where path like '5' || '/' || '%'
group by child;

count(*)计算特定孩子的后代数,包括该孩子。因此,- 1来获取他们后代的数量。

这应该有效,因此您可以用任何路径替换'5'