Извлечение поддерева из дерева иерархии на основе листа в Oracle

#oracle #tree #hierarchical-data #connect-by

#Oracle #дерево #иерархия-данные #подключение по

Вопрос:

У меня есть таблица users , представляющая иерархическое дерево следующим образом:

Столбец Тип Комментарий
идентификатор пользователя целое число последовательность
user_type целое число 1 для группы пользователей 2 для обычного пользователя
group_id целое число Ссылка на пользователя в той же таблице с user_type = 1
имя_пользователя varchar(xxx)

Столбец group_id ссылается на другой user_id, так что группы и пользователи хранятся в одной таблице.

Идентификатор главной группы равен 0.

Вот так:

идентификатор пользователя user_type group_id имя_пользователя
0 1 null «Все пользователи»
5 2 0 ‘ПОЛЬЗОВАТЕЛЬ1’
6 2 0 ‘USER2’
11 1 0 ‘SUBGROUP1’
12 1 11 ‘SUBGROUP2’
13 2 12 ‘ПОЛЬЗОВАТЕЛЬ3’
20 1 0 ‘SUBGROUP3’
21 2 20 ‘ПОЛЬЗОВАТЕЛЬ4’

Обратите внимание, что:

  • В user_id могут быть пробелы.
  • Группа может содержать ничего или любое количество групп или пользователей.

Мне уже удалось получить полное дерево с правильным отступом и отсортированным, используя connect by инструкцию oracle.

Это не мой вопрос здесь.

Мой вопрос:

Учитывая идентификатор пользователя для запроса, как просмотреть дерево до главной группы «Все пользователи» и вывести в результате полный путь от листа до главной группы?

Пример 1: Я запускаю запрос для USER1, мне нужен следующий вывод:

 All Users
- USER1
 

Пример 2: Я запускаю тот же запрос для USER3, мне нужен следующий вывод:

 All Users
- SUBGROUP1
-- SUBGROUP2
--- USER3
 

Я надеюсь, что кто-нибудь может мне помочь в этом.

Для получения дополнительной информации я отправляю запрос для получения полного дерева, чтобы вы могли увидеть использование connect by и start with . Я уверен, что этот запрос близок к тому, который я хочу, но мои попытки никогда не дают желаемого результата.

 select 
  lpad('-', (level - 1) * 2, ' ') || u.user_name as padded_name, 
  u.userid, 
  u.user_group, 
  u.user_type,
  level
from users u
connect by prior u.user_id = u.group_id
start with u.user_id = 0
order siblings by upper(u.user_name);
 

Ответ №1:

Вы могли бы использовать connect by для перемещения в противоположном направлении. Тогда level , конечно, тоже будет наоборот. Итак, чтобы получить результаты в правильном порядке и с правильным отступом, создайте другой запрос на основе этих результатов, который будет использоваться row_number() для определения отступа:

 with base as (
  select 
    u.user_name,
    u.user_id, 
    u.group_id, 
    u.user_type,
    level as lvl
  from users u
  connect by prior u.group_id = u.user_id
  start with u.user_id = 13
)
select 
  lpad('-', (row_number() over (order by lvl desc) - 1) * 2, ' ') || base.user_name
    as padded_name,
  user_id,
  group_id,
  user_type
from base
order by lvl desc;