В качестве довольно грубого подхода вы можете использовать иерархический запрос или рекурсивный CTE, чтобы развернуть все исходные диапазоны дат в одну строку на сотрудника в день:
with rcte1 (emp_id, mgr_id, dt, to_dt) as (
select emp_id, mgr_id, frm_dt, to_dt
from emp_mgr_relation
union all
select emp_id, mgr_id, dt + 1, to_dt
from rcte1
where to_dt > dt
)
select emp_id, mgr_id, dt from rcte1 order by dt, emp_id, mgr_id;
EMP_ID MGR_ID DT
------------------------------ ------------------------------ ----------
EMP1 MGR1 2018-01-01
EMP2 MGR2 2018-01-01
EMP3 MGR3 2018-01-01
EMP4 MGR4 2018-01-01
EMP5 MGR5 2018-01-01
EMP6 MGR6 2018-01-01
EMP1 MGR1 2018-01-02
EMP2 MGR2 2018-01-02
...
EMP6 MGR2 2018-01-30
EMP1 MGR1 2018-01-31
EMP2 MGR2 2018-01-31
EMP3 MGR3 2018-01-31
EMP4 MGR4 2018-01-31
EMP5 MGR4 2018-01-31
EMP6 MGR2 2018-01-31
184 rows selected.
, а затем объединить их по менеджеру и дате:
with rcte1 (emp_id, mgr_id, dt, to_dt) as (
select emp_id, mgr_id, frm_dt, to_dt
from emp_mgr_relation
union all
select emp_id, mgr_id, dt + 1, to_dt
from rcte1
where to_dt > dt
),
cte2 (mgr_id, dt, sub_ord_cn, subordinates) as (
select mgr_id, dt, count(*), listagg (emp_id, ',') within group (order by emp_id)
from rcte1
group by mgr_id, dt
)
select * from cte2 order by mgr_id, dt;
MGR_ID DT SUB_ORD_CN SUBORDINATES
------------------------------ ---------- ---------- ------------------------------
MGR1 2018-01-01 1 EMP1
MGR1 2018-01-02 1 EMP1
MGR1 2018-01-03 1 EMP1
...
MGR1 2018-01-10 1 EMP1
MGR1 2018-01-11 2 EMP1,EMP5
MGR1 2018-01-12 2 EMP1,EMP5
MGR1 2018-01-13 2 EMP1,EMP5
...
149 rows selected.
и затем применить Табибитозан :
with rcte1 (emp_id, mgr_id, dt, to_dt) as (
select emp_id, mgr_id, frm_dt, to_dt
from emp_mgr_relation
union all
select emp_id, mgr_id, dt + 1, to_dt
from rcte1
where to_dt > dt
),
cte2 (mgr_id, dt, sub_ord_cn, subordinates) as (
select mgr_id, dt, count(*), listagg (emp_id, ',') within group (order by emp_id)
from rcte1
group by mgr_id, dt
),
cte3 (mgr_id, dt, sub_ord_cn, subordinates, bucket) as (
select mgr_id, dt, sub_ord_cn, subordinates,
row_number() over (partition by mgr_id, sub_ord_cn, subordinates order by dt)
- row_number() over (partition by mgr_id order by dt)
from cte2
)
select * from cte3 order by mgr_id, dt;
MGR_ID DT SUB_ORD_CN SUBORDINATES BUCKET
------------------------------ ---------- ---------- ------------------------------ ----------
MGR1 2018-01-01 1 EMP1 0
MGR1 2018-01-02 1 EMP1 0
...
MGR1 2018-01-10 1 EMP1 0
MGR1 2018-01-11 2 EMP1,EMP5 -10
...
MGR1 2018-01-15 2 EMP1,EMP5 -10
MGR1 2018-01-16 1 EMP1 -5
...
MGR6 2018-01-15 1 EMP6 0
149 rows selected.
, а затем объединить эти группы менеджеров:
with rcte1 (emp_id, mgr_id, dt, to_dt) as (
select emp_id, mgr_id, frm_dt, to_dt
from emp_mgr_relation
union all
select emp_id, mgr_id, dt + 1, to_dt
from rcte1
where to_dt > dt
),
cte2 (mgr_id, dt, sub_ord_cn, subordinates) as (
select mgr_id, dt, count(*), listagg (emp_id, ',') within group (order by emp_id)
from rcte1
group by mgr_id, dt
),
cte3 (mgr_id, dt, sub_ord_cn, subordinates, bucket) as (
select mgr_id, dt, sub_ord_cn, subordinates,
row_number() over (partition by mgr_id, sub_ord_cn, subordinates order by dt)
- row_number() over (partition by mgr_id order by dt)
from cte2
)
select mgr_id, min(dt) as frm_dt, max(dt) as to_dt, sub_ord_cn, subordinates
from cte3
group by mgr_id, bucket, sub_ord_cn, subordinates
order by mgr_id, frm_dt;
, который получает:
MGR_ID FRM_DT TO_DT SUB_ORD_CN SUBORDINATES
------------------------------ ---------- ---------- ---------- ------------------------------
MGR1 2018-01-01 2018-01-10 1 EMP1
MGR1 2018-01-11 2018-01-15 2 EMP1,EMP5
MGR1 2018-01-16 2018-01-31 1 EMP1
MGR2 2018-01-01 2018-01-15 1 EMP2
MGR2 2018-01-16 2018-01-17 2 EMP2,EMP5
MGR2 2018-01-18 2018-01-20 3 EMP2,EMP5,EMP6
MGR2 2018-01-21 2018-01-31 2 EMP2,EMP6
MGR3 2018-01-01 2018-01-20 1 EMP3
MGR3 2018-01-21 2018-01-25 2 EMP3,EMP5
MGR3 2018-01-26 2018-01-31 1 EMP3
MGR4 2018-01-01 2018-01-25 1 EMP4
MGR4 2018-01-26 2018-01-31 2 EMP4,EMP5
MGR5 2018-01-01 2018-01-10 1 EMP5
MGR6 2018-01-01 2018-01-15 1 EMP6
14 rows selected.
Если вы используете версию с ошибками в рекурсивном CTE (11.2.0.2, похоже, возвращает только 11 строк, вероятно, из-за 11840579, который был исправлен в 11.2.0.3), вы можете вместо этого использовать иерархический запрос; что-то вроде:
with cte1 (emp_id, mgr_id, dt) as (
select emp_id, mgr_id, frm_dt + level - 1
from emp_mgr_relation
connect by emp_id = prior emp_id
and mgr_id = prior mgr_id
and frm_dt = prior frm_dt
and prior dbms_random.value is not null
and level <= to_dt - frm_dt + 1 --correction here
),
cte2 (mgr_id, dt, sub_ord_cn, subordinates) as (
select mgr_id, dt, count(*), listagg (emp_id, ',') within group (order by emp_id)
from cte1
group by mgr_id, dt
),
cte3 (mgr_id, dt, sub_ord_cn, subordinates, bucket) as (
select mgr_id, dt, sub_ord_cn, subordinates,
row_number() over (partition by mgr_id, sub_ord_cn, subordinates order by dt)
- row_number() over (partition by mgr_id order by dt)
from cte2
)
select mgr_id, min(dt) as frm_dt, max(dt) as to_dt, sub_ord_cn, subordinates
from cte3
group by mgr_id, bucket, sub_ord_cn, subordinates
order by mgr_id, frm_dt;
, который получает тот же результат.