【问题标题】:Dynamic table created from CTE (parent/child)从 CTE(父/子)创建的动态表
【发布时间】:2016-06-01 06:25:08
【问题描述】:

如果我有一个名为tree的非常简单的表

create table if not exists tree (id int primary key, parent int, name text);

还有几行数据

insert into tree values (1, null, 'A');
insert into tree values (2, 1, 'B');
insert into tree values (3, 1, 'C');
insert into tree values (4, 2, 'D');
insert into tree values (5, 2, 'E');
insert into tree values (6, 3, 'F');
insert into tree values (7, 3, 'G');

我可以轻松地在其上运行 CTE,并生成一个输出给我这样的路径

with recursive R(id, level, path, name) as (
    select id,1,name,name from tree where parent is null
    union select tree.id, level + 1, path || '.' || tree.name, tree.name from tree join R on R.id=tree.parent
) select level,path,name from R;

这给出了输出

level  | path  | name 
-------+-------+------
     1 | A     | A
     2 | A.B   | B
     2 | A.C   | C
     3 | A.B.D | D
     3 | A.B.E | E
     3 | A.C.F | F
     3 | A.C.G | G

我想知道的是,是否可以以某种方式将此输出投影到另一个表中,根据级别(级别 1、级别 2、级别 3 等)动态创建列,作为回报给我这样的东西

id | level1 | level2 | level3
---+--------+--------+-------
 1 | A      |        |
 2 | A      | B      |
 3 | A      | C      |
 4 | A      | B      | D
 5 | A      | B      | E
 6 | A      | C      | F
 7 | A      | C      | G

任何帮助将不胜感激。

【问题讨论】:

    标签: sql postgresql pivot common-table-expression crosstab


    【解决方案1】:

    如果您知道树的最大深度,我会保留您的方法并使用数组连接对其进行简化以产生所需的输出。 所以对于一个 5 级树,它看起来像这样:

    WITH RECURSIVE R(id, path) AS (
        SELECT id, ARRAY[name::text] FROM tree WHERE parent IS NULL
        UNION SELECT tree.id, path || tree.name FROM tree JOIN R ON R.id=tree.parent
    ) 
    SELECT id,
        path[1] AS l1, 
        path[2] AS l2, 
        path[3] AS l3, 
        path[4] AS l4, 
        path[5] AS l5
    FROM R;
    

    PS:很抱歉没有评论 Ziggy 非常接近的答案,但我没有足够的声誉这样做。我不明白你为什么需要一个窗口函数?

    【讨论】:

    • 其实我不知道最大深度是多少。开始认为我可能需要创建一个表来保留我的 CTE 输出,然后使用一个函数来创建 + 填充表(我想真正的答案是这在 SQL 中是不可能的)。
    • 使用 PL/pgSQL 函数绝对是 PostgreSQL 的选择。但是你真的需要动态列吗?如果这个输出很难创建,它也很难使用,除非你只使用SELECT * 语句。
    【解决方案2】:

    PostgreSQL 要求始终定义输出的类型,因此您不能动态生成列levelX。但是,您可以执行以下操作:

    with recursive
      R(id, path) as (
        select id,ARRAY[name::text] from tree where parent is null
        union 
        select tree.id, path || tree.name::text from tree join R on R.id=tree.parent
        )
    select row_number() over (order by cardinality(path), path), id,
           path[1] as level1, path[2] as level2, path[3] as level3
    from R
    order by 1
    

    在上面的示例中,row_number 列恰好与 id 匹配,但您的真实数据可能不会发生这种情况。

    【讨论】:

      猜你喜欢
      • 2012-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      相关资源
      最近更新 更多