Сделать простую таблицу из иерархической таблицы в Postgresql - PullRequest
0 голосов
/ 01 февраля 2020

У меня есть таблица с полями id, parent_id и name, мне нужно преобразовать ее в "обычный" вид. Пример:

Таблица "t_obj" с двухуровневой иерархией:

Id Parent_Id Name
1  0          Build1
2  1          Work1
3  1          Work2   
4  0          Build2
5  4          Work1
6  4          Work2  

Результат "plain" -table:

Level1     Level2      
Build1     Work1   
Build1     Work2  
Build2     Work1  
Build2     Work2   

Сделать эту таблицу легко через это SQL:

SELECT 
  public.t_obj."Name",
  t_obj1."Name"
FROM
  public.t_obj t_obj1
  INNER JOIN public.t_obj ON (t_obj1."Parent_id" = public.t_obj."Id")

Вопрос в том, как сделать этот результат («простой» -табильный), если у меня иерархия N-уровня? Например, для этой таблицы:

Id Parent_Id Name
1  0          Build1
2  1          Work1
3  1          Work2   
4  0          Build2
5  4          Work1
6  4          Work2  
7  6          SubWork1
8  7          SubSubWork1
9  8          SubSubSubWork1
........

Результат должен быть примерно таким:

Level1     Level2  Level3    Level4       Level5    
Build1     Work1   Null      Null         Null
Build1     Work2   Null      Null         Null
Build2     Work1   Null      Null         Null 
Build2     Work2   SubWork1  SubSubWork1  SubSubSubWork1

Но я понятия не имею, как написать запрос SQL, чтобы получить этот результат.

1 Ответ

0 голосов
/ 01 февраля 2020

Для этого вам нужен рекурсивный CTE.

Следующее в основном делает то, что вы хотите, но оно помещает информацию об иерархии в массив, а не в отдельные столбцы. Это делает его гораздо более расширяемым для более глубоких иерархий:

with recursive cte as (
      select id as leaf_id, parent_id, name, array[name] as names, 1 as lev
      from t_obj t
      where not exists (select 1 from t_obj t2 where t2.parent_id = t.id)
      union all
      select cte.leaf_id, t.parent_id, t.name, array(t.name) || cte.names, cte.lev + 1
      from cte join
           t_obj t
           on cte.parent_id = t.id
     )
select distinct on (leaf_id) cte.*
from cte
order by leaf_id, lev desc;

Конечно, вы можете извлекать элементы массива в отдельные столбцы, если хотите.

Здесь - это db <> fiddle.

EDIT:

Вы можете извлечь значения следующим образом:

select distinct on (leaf_id) name, names[2], names[3], names[4]
from cte
order by leaf_id, lev desc

Здесь - это скрипта db <>.

...