Sql 从CTE创建的动态表(父/子)

Sql 从CTE创建的动态表(父/子),sql,postgresql,pivot,common-table-expression,crosstab,Sql,Postgresql,Pivot,Common Table Expression,Crosstab,如果我有一个非常简单的表,名为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, '

如果我有一个非常简单的表,名为
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
我想知道的是,是否有可能以某种方式将此输出投影到另一个表中,根据级别(level1、level2、level3等)动态创建列,从而得到类似的结果

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

任何帮助都将不胜感激。

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

在上面的示例中,列
行号
恰好与
id
匹配,但实际数据可能不会匹配。

如果您知道树的最大深度,我会保留您的方法,并使用数组连接简化它,以生成所需的输出。 因此,对于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:很抱歉没有对齐吉的答案发表评论,这个答案非常接近,但我没有足够的声誉这么做。我不明白您为什么需要在这里使用窗口功能?

实际上,我不知道最大深度是多少。我开始认为我可能需要创建一个表来保存我的CTE输出,然后使用一个函数来创建并填充该表(我想真正的答案是,这在SQL中是不可能的)。使用PL/pgSQL函数肯定是PostgreSQL的方法。但是您真的需要动态列吗?如果这个输出很难创建,那么它也很难使用,除非您只使用
SELECT*
语句。