PostgreSQL查询可检索按深度排序的所有子项的父项

时间:2018-10-29 22:12:18

标签: sql database postgresql select tree

假设我有以下表格:

帐户

parent_id | id

......
10         | 101
20         | 201
30         | 301
30         | 302
40         | 401
40         | 402
401        | 4011
401        | 4012
4012       | 40121

和表 accounts_tree

ancestor | descentant | depth

1         | 10         | 1
1         | 20         | 1
1         | 30         | 1
1         | 40         | 1
1         | 101        | 2
1         | 201        | 2
1         | 301        | 2
1         | 302        | 2
1         | 401        | 2
1         | 402        | 2
1         | 4011       | 3
1         | 4012       | 3
1         | 40121      | 4
10        | 101        | 1
20        | 201        | 1
30        | 301        | 1
30        | 302        | 1
40        | 401        | 1
40        | 402        | 1
40        | 4011       | 2
40        | 4012       | 2
40        | 40121      | 3
401       | 4011       | 1
401       | 4012       | 1
4012      | 40121      | 1

我需要的是与所有孩子一起显示帐户ID(父母),并为每个孩子显示他们的孩子(按深度递增分组); 到目前为止,我使用过:

SELECT
    a.parent_id,
    a.id,
    p.depth
FROM
    accounts a
    INNER JOIN account_tree p ON a.id = p.descendant
    WHERE
        p.ancestor = 1
        AND p.depth <= 4
    ORDER BY
        a.parent_id;

将返回按父ID排序的所有帐户。 我的期望是:

parent_id | id        | depth

1         | 10         | 1
10        | 101        | 2
1         | 20         | 1
20        | 201        | 2
1         | 30         | 1
30        | 301        | 2
30        | 302        | 2
1         | 40         | 1
40        | 401        | 2
401       | 4011       | 3
401       | 4012       | 3
4012      | 40121      | 4
40        | 402        | 2

我必须提到,在我正在从事的项目中,有500多个帐户,并且它们的ID并不是像我的示例中那样“可预测的”编号,并且深度超过5个级别。

2 个答案:

答案 0 :(得分:1)

使TomC适应Postgres

with recursive tree as (
    select parent_id, id, lpad(id::varchar(12),12,'0')::varchar(144) as idPath, 1::int as depth 
    from accounts 
    where parent_id = '1'
    union all 
    select a.parent_id, a.id, concat(idPath, lpad(a.id,12,'0'))::varchar(144)  idPath, depth + 1::int as depth
    from accounts a
    join tree on tree.id=a.parent_id
)
select parent_id, id, depth, idpath
from tree 
order by idpath

如果您使用lpad()在任何级别使用不同长度的帐户字符串,请不要偏向整体顺序。您需要选择适合您实际帐号的长度。我用过12,并且串联的路径必须是您选择的任何数字的倍数。

+----+-----------+-------+-------+--------------------------------------------------+
|    | parent_id | id    | depth | idpath                                           |
+----+-----------+-------+-------+--------------------------------------------------+
| 1  | 1         | 10    | 1     | 000000000010                                     |
| 2  | 10        | 101   | 2     | 000000000010000000000101                         |
| 3  | 1         | 20    | 1     | 000000000020                                     |
| 4  | 20        | 201   | 2     | 000000000020000000000201                         |
| 5  | 1         | 30    | 1     | 000000000030                                     |
| 6  | 30        | 301   | 2     | 000000000030000000000301                         |
| 7  | 30        | 302   | 2     | 000000000030000000000302                         |
| 8  | 1         | 40    | 1     | 000000000040                                     |
| 9  | 40        | 401   | 2     | 000000000040000000000401                         |
| 10 | 401       | 4011  | 3     | 000000000040000000000401000000004011             |
| 11 | 401       | 4012  | 3     | 000000000040000000000401000000004012             |
| 12 | 4012      | 40121 | 4     | 000000000040000000000401000000004012000000040121 |
| 13 | 40        | 402   | 2     | 000000000040000000000402                         |
+----+-----------+-------+-------+--------------------------------------------------+

使用的样本数据:

CREATE TABLE accounts(
   parent_id VARCHAR(12) 
  ,id        VARCHAR(12) 
);
INSERT INTO accounts(parent_id,id) VALUES ('1','10');
INSERT INTO accounts(parent_id,id) VALUES ('1','20');
INSERT INTO accounts(parent_id,id) VALUES ('1','30');
INSERT INTO accounts(parent_id,id) VALUES ('1','40');
INSERT INTO accounts(parent_id,id) VALUES ('10','101');
INSERT INTO accounts(parent_id,id) VALUES ('20','201');
INSERT INTO accounts(parent_id,id) VALUES ('30','301');
INSERT INTO accounts(parent_id,id) VALUES ('30','302');
INSERT INTO accounts(parent_id,id) VALUES ('40','401');
INSERT INTO accounts(parent_id,id) VALUES ('40','402');
INSERT INTO accounts(parent_id,id) VALUES ('401','4011');
INSERT INTO accounts(parent_id,id) VALUES ('401','4012');
INSERT INTO accounts(parent_id,id) VALUES ('4012','40121');

答案 1 :(得分:0)

这是递归CTE的情况。我假设您的键实际上是varchar,因为它们不代表数字值-如果不是,则按照我的上一篇文章进行更新。

这将创建测试数据。您只需要帐户表,而不需要树形表

create table #account(parent_id varchar(10), id varchar(10))
insert #account values ('1','10'),('1','20'),('1','30'),('1','40'),('10','101'),('20','201'),('30','301')
    ,('30','302'),('40','401'),('40','402'),('401','4011'),('401','4012'),('4012','40121')

现在,您进行递归查询,首先找到所有顶层行(父级= 1),然后找到所有子级,并逐步构建复合路径

;with tree as (
    select parent_id, id, convert(varchar(100),id) as idPath, 1 as depth 
    from #account 
    where parent_id=1
    union all 
    select a.parent_id, a.id, convert(varchar(100),idPath+a.id) as idPath, depth+1 as depth
    from #account a
    join tree on tree.id=a.parent_id
)
select parent_id, id, depth from tree order by idpath

如果ID实际上是整数数据类型,则将串联更改为

convert(varchar(100),convert(varchar(10),idPath)+convert(varchar(10),a.id)) as idPath