【问题标题】:Postgresql query to retrieve parents with all children orderered by depthPostgresql 查询以检索具有按深度排序的所有孩子的父母
【发布时间】:2019-04-02 22:11:12
【问题描述】:

假设我有以下表格:

帐户

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 个级别。

【问题讨论】:

    标签: sql database postgresql select tree


    【解决方案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');
    

    【讨论】:

    • 您在 OP 的问题中发现了一个有趣的问题 - 无论是否填充整数,样本数据都可以工作 - 看看是否需要它很有趣。大概是吧。感谢 Postgres 版本。
    【解决方案2】:

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

    这将创建测试数据。你只需要你的 Account 表,而不是你的树表

    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
    

    【讨论】:

    • 所有数据都是大整数,深度是整数。我的帐户是这样的(示例): parent_id 807 有孩子喜欢 23456, 39887,12345
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-13
    • 2019-05-02
    • 2019-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多