Выберите запрос с рекурсией - PullRequest
2 голосов
/ 06 февраля 2020

У меня есть две таблицы, содержащие имена сотрудников (TableB) и иерархию сотрудников (TableA) (имя_управления из TableA может быть employee_id в той же таблице).

TableA
UniqueId    Employee_ID Manager_ID
1            101         102
2            102         103
3            103         104
4            105         106
5            106         null


TableB
Employee_ID  Employee_Name
101          First
102          Second
103          Third
104          Fourth
105          Fifth
106          Sixth

, и мне нужен вывод, как показано ниже:

Employee_ID Employee_Name   Transferred
101         First           True
102         Second          True
103         Third           True
105         Fifth           False
106         Sixth           False

Столбец Передано для каждого сотрудника рассчитывается как =

isTransferred(Employee_ID)
{
    If(Manager_ID  is null) return false;
    If(Manager_ID is present as employee_id in table A)
    {
       return isTransferred(manager_ID)
    }
    else
    {
       return true;
    }
}

Есть ли способ получить результат в одном утверждении выбора?

Ответы [ 2 ]

2 голосов
/ 07 февраля 2020

Вы можете использовать рекурсивный CTE, а затем получить последний уровень «рекурсии» для каждого сотрудника. Получив это, вы просто проверяете manager_id последнего уровня, чтобы узнать, передан ли он.

Например:

with
tablea as (
  select 1 as uniqueId, 101 as employee_id, 102 as manager_id from dual union all
  select 2  as uniqueId, 102 as employee_id, 103 as manager_id from dual union all
  select 3  as uniqueId, 103 as employee_id, 104 as manager_id from dual union all
  select 4  as uniqueId, 105 as employee_id, 106 as manager_id from dual union all
  select 5  as uniqueId ,106 as employee_id, null from dual 
),
tableb as (
  select 101 as employee_id, 'first' as employee_name from dual union all
  select 102 as employee_id, 'second' as employee_name from dual union all
  select 103 as employee_id, 'third' as employee_name from dual union all
  select 104 as employee_id, 'fourth' as employee_name from dual union all
  select 105 as employee_id, 'fifth' as employee_name from dual union all
  select 106 as employee_id, 'sixth' as employee_name from dual 
),
n (employee_id, employee_name, lvl, manager_id) as (
  select b.employee_id, b.employee_name, 1, a.manager_id
  from tablea a
  join tableb b on a.employee_id = b.employee_id
union all
  select
    n.employee_id, n.employee_name, lvl + 1, a.manager_id
  from n
  join tablea a on a.employee_id = n.manager_id
),
m (employee_id, max_lvl) as (
  select employee_id, max(lvl) from n group by employee_id
)
select n.employee_id, n.employee_name, 
  case when n.manager_id is not null then 'True' else 'False' end as transferred
from n
join m on n.employee_id = m.employee_id and n.lvl = m.max_lvl
order by n.employee_id

Результат:

EMPLOYEE_ID  EMPLOYEE_NAME  TRANSFERRED
-----------  -------------  -----------
        101  first          True       
        102  second         True       
        103  third          True       
        105  fifth          False      
        106  sixth          False      
1 голос
/ 07 февраля 2020

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

  • Внешнее присоединение цепочки управления к сотрудникам
  • Найти сотрудников без менеджера, сопоставив unique_id к «невозможному» значению, например, -1
  • Используйте результаты этого объединения, чтобы пройтись по дереву, начиная со строк, в которых менеджер имеет значение null
  • Получить значение для выражения в шаг 2 для root строк; если это «невозможное» значение, тогда передайте => true
  • Отфильтруйте строки для невозможного значения

Что дает что-то вроде:

with table_a ( UniqueId, Employee_ID, Manager_ID ) as (
  select 1, 101, 102 from dual union all 
  select 2, 102, 103 from dual union all 
  select 3, 103, 104 from dual union all
  select 4, 105, 106 from dual union all 
  select 5, 106, null from dual 
), Table_b ( Employee_ID, Employee_Name ) as (
  select 101, 'First' from dual union all
  select 102, 'Second' from dual union all
  select 103, 'Third' from dual union all
  select 104, 'Fourth' from dual union all
  select 105, 'Fifth' from dual union all
  select 106, 'Sixth' from dual
), rws as (
  select b.*, a.Manager_ID,
         nvl ( a.UniqueId, -1 ) transfer
  from   table_b b
  left join table_a a
  on   b.Employee_ID = a.Employee_ID
)
  select r.*, 
         case connect_by_root transfer
           when -1 then 'true'
           else 'false'
         end transferred
  from   rws r
  where  transfer > 0
  start  with manager_id is null
  connect by manager_id = prior employee_id;

EMPLOYEE_ID    EMPLOYEE_NAME    MANAGER_ID    TRANSFER    TRANSFERRED   
           103 Third                      104           3 true           
           102 Second                     103           2 true           
           101 First                      102           1 true           
           106 Sixth                   <null>           5 false          
           105 Fifth                      106           4 false 
...