Ваша таблица Sys_User
представляет собой список смежных групп, в котором содержится только информация о сотрудниках и о которых они напрямую отчитываются. Списки смежности являются одним из способов кодирования иерархических данных. Они хороши, потому что они относительно быстрые и компактные, однако они не являются единственным способом кодирования иерархических отношений.
Чтобы ответить на вопросы, которые вы задаете, вам будет полезно перекодировать данные в таблицу закрытия, которая отображает каждого сотрудника на всех его прямых и косвенных менеджеров / репортеров вместе с их степенью разделения, а также любую другую дополнительную соответствующую информацию , Однако, поскольку он представляет отношение «многие ко многим», вы не хотите перегружать его слишком большим количеством дополнительных данных. К счастью, благодаря утилите рекурсивных запросов вы можете легко создать ее на лету.
Чтобы создать таблицу замыкания, вы начинаете с ее заполнения отношениями степени 0, где каждый сотрудник считается своим менеджером / репортером. Обоснование этого немного выходит за рамки моего понимания, но оно как-то связано с математикой, лежащей в основе концепции транзитивного замыкания (отсюда и таблица замыканий имен). После этого вы многократно (рекурсивно) добавляете каждую дополнительную степень отчетности. Вы можете сделать это сверху вниз или снизу вверх
Вот версия сверху вниз :
with closure(manager_id, report_id, degree, is_managing_SVP, is_reporting_svp) as (
select sys_id
, sys_id
, 0
, case when title like '%SVP%' then 1 else 0 end
, case when title like '%SVP%' then 1 else 0 end
from sys_user
union all
select cur.manager_id
, nxt.sys_id
, cur.degree+1
, cur.is_managing_SVP
, case when nxt.title like '%SVP%' then 1 else 0 end
from closure cur
join sys_user nxt
on nxt.manager = cur.report_id
and nxt.sys_id <> nxt.manager
)
select * from closure
А вот версия Bottom Up :
with closure(manager_id, report_id, degree, is_managing_SVP, is_reporting_svp) as (
select sys_id
, sys_id
, 0
, case when title like '%SVP%' then 1 else 0 end
, case when title like '%SVP%' then 1 else 0 end
from sys_user
union all
select nxt.manager
, cur.report_id
, cur.degree+1
, case when mgr.title like '%SVP%' then 1 else 0 end
, cur.is_reporting_SVP
from closure cur
join sys_user nxt
on nxt.sys_id = cur.manager_id
and nxt.sys_id <> nxt.manager
join sys_user mgr
on mgr.sys_id = nxt.manager
)
select * from closure
Не имеет большого значения, какую версию вы используете, если вы собираетесь сгенерировать всю таблицу закрытия, однако, если вы хотите оптимизировать свой запрос и сгенерировать только частичную таблицу закрытия, то это зависит от того, хотите ли вы идти вверх или вниз по дереву.
После создания вы можете использовать таблицу закрытия, чтобы ответить на ваши вопросы о SVP, например, кто является SVP каждого подрядчика:
select r.sys_id, r.name, r.title, c.degree
, c.manager_id SVP_ID
, m.name SVP_name
, m.title SVP_title
from sys_user r
join closure c
on c.report_id = r.sys_id
join sys_user m
on m.sys_id = c.manager_id
where r.title like '%contractor%'
and c.is_managing_svp = 1
sys_id | name | title | degree | SVP_ID | SVP_name | SVP_title
-----: | :----------- | :--------- | -----: | -----: | :---------------- | :--------
555789 | Tina Belcher | Contractor | 2 | 654321 | Calvin Fischoeder | SVP
Или каждый прямой и косвенный отчет в СВП по имени Кельвин Фишхоудер:
select m.sys_id manager_id
, m.name
, m.title
, c.degree
, r.sys_id report_id
, r.name report_name
, r.title report_title
from sys_user m
join closure c
on c.manager_id = m.sys_id
join sys_user r
on r.sys_id = c.report_id
where m.name = 'Calvin Fischoeder'
order by degree, report_name
manager_id | name | title | degree | report_id | report_name | report_title
---------: | :---------------- | :---- | -----: | --------: | :---------------- | :-----------
654321 | Calvin Fischoeder | SVP | 0 | 654321 | Calvin Fischoeder | SVP
654321 | Calvin Fischoeder | SVP | 1 | 123456 | Bob Belcher | Manager
654321 | Calvin Fischoeder | SVP | 2 | 555789 | Tina Belcher | Contractor
Чтобы увидеть все запросы в действии, проверьте это db <> fiddle