Wanted to check whether the employee got rehired to a contract or not. If he is rehired then return the rehire period . If the multiple employees got rehired then return all their rehire period .
Sample data:(Table 'Contract')
Employee_id Period Contract
111 202204 1NA
111 202205 1NA
111 202206 1NA
112 202207 1NA
112 202208 1NA
111 202209 1NA
In the above case the output should be ,
Employee_id Period Contract
111 202209 1NA
The query should first check whether the employee got rehired or not, if so then return the rehire period. If that contract has got no rehire's then return NULL.
Any other logic other than lag n lead will also be Appreciated!
Thanks in advance:)
CodePudding user response:
Use LAG
to identify if the previous period interval, then only select the ones where the interval > 1
create table contracts (employee_id,period,contract) as
(
SELECT 111, 202204,'1NA' FROM DUAL UNION ALL
SELECT 111, 202205,'1NA' FROM DUAL UNION ALL
SELECT 111, 202206,'1NA' FROM DUAL UNION ALL
SELECT 111, 202209,'1NA' FROM DUAL UNION ALL
SELECT 112, 202207,'1NA' FROM DUAL UNION ALL
SELECT 112, 202208,'1NA' FROM DUAL
);
Table CONTRACTS created.
with contracts_w_lags (
employee_id
,period
,last_period
,contract
) as ( select employee_id
,period
,lag(period)
over(partition by employee_id
order by period)
,contract
from contracts
)
select employee_id
,period
,contract
from contracts_w_lags
where period - nvl( last_period ,period ) > 1;
EMPLOYEE_ID PERIOD CON
----------- ---------- ---
111 202209 1NA
Note that your sample data only has periods within the same year. This example will fail if periods cross years.
To overcome that, create a pseudo "periods" table with a rownumber to identify consecutive rows:
create table contracts (employee_id,period,contract) as
(
SELECT 111, 202111,'1NA' FROM DUAL UNION ALL
SELECT 111, 202112,'1NA' FROM DUAL UNION ALL
SELECT 111, 202201,'1NA' FROM DUAL UNION ALL
SELECT 111, 202203,'1NA' FROM DUAL UNION ALL
SELECT 112, 202207,'1NA' FROM DUAL UNION ALL
SELECT 112, 202208,'1NA' FROM DUAL
);
Table CONTRACTS created.
with month_count ( cnt ) as
( select months_between(
to_date( max(period) ,'YYYYMM' )
,to_date( min(period) ,'YYYYMM' ))
from contracts
),contract_start ( dt ) as
( select to_date( min(period) ,'YYYYMM' )
from contracts
),contract_periods ( period ,rn ) as
( select to_char( add_months( c.dt ,level - 1 ) ,'YYYYMM' )
,row_number() over( order by add_months( c.dt ,level - 1 ) )
from contract_start c
,month_count m
connect by level <= m.cnt 1
),contracts_w_lags ( employee_id ,period ,contract ,period_rn ,last_period_rn ) as
( select c.employee_id
,c.period
,c.contract
,p.rn
,lag(p.rn) over(partition by c.employee_id order by p.rn )
from contracts c
join contract_periods p on c.period = p.period
)
select employee_id
,period
,contract
from contracts_w_lags
where period_rn - nvl( last_period_rn ,period_rn ) > 1;
EMPLOYEE_ID PERIOD CON
----------- ---------- ---
111 202209 1NA
CodePudding user response:
The answer (after comments) is at the end....
It is unclear what is your expected result with this sample data:
WITH
contracts (EMP_ID, PERIOD, CONTRACT) as
(
SELECT 111, 202204, '1NA' FROM DUAL UNION ALL
SELECT 111, 202205, '1NA' FROM DUAL UNION ALL
SELECT 111, 202206, '1NA' FROM DUAL UNION ALL
SELECT 112, 202207, '1NA' FROM DUAL UNION ALL
SELECT 112, 202208, '1NA' FROM DUAL UNION ALL
SELECT 111, 202209, '1NA' FROM DUAL
)
There are some multiple consecutive periods for both sample eployees. One of the options is to show first and last periods for emps with multiple periods:
SELECT EMP_ID, Min(PREV_PERIOD) "FIRST_PERIOD", Max(PERIOD) "LAST_PERIOD", CONTRACT
FROM (Select EMP_ID, PERIOD, CONTRACT,
LAG(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "PREV_PERIOD"
From contracts)
WHERE PREV_PERIOD != 0
GROUP BY EMP_ID, CONTRACT
--
-- R e s u l t :
-- EMP_ID FIRST_PERIOD LAST_PERIOD CONTRACT
-- ---------- ------------ ----------- --------
-- 111 202204 202209 1NA
-- 112 202207 202208 1NA
... another could be to show them all :
SELECT EMP_ID, PERIOD "PERIOD", PREV_PERIOD "PREV_PERIOD", CONTRACT
FROM (Select EMP_ID, PERIOD, CONTRACT,
LAG(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "PREV_PERIOD"
From contracts)
WHERE PREV_PERIOD != 0
--
-- R e s u l t :
-- EMP_ID PERIOD PREV_PERIOD CONTRACT
-- ---------- ---------- ----------- --------
-- 111 202205 202204 1NA
-- 111 202206 202205 1NA
-- 111 202209 202206 1NA
-- 112 202208 202207 1NA
... and if you want the same with LEAD() function
SELECT EMP_ID, PERIOD "PERIOD", NEXT_PERIOD "NEXT_PERIOD", CONTRACT
FROM (Select EMP_ID, PERIOD, CONTRACT,
LEAD(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "NEXT_PERIOD"
From contracts)
WHERE NEXT_PERIOD != 0
--
-- R e s u l t :
-- EMP_ID PERIOD NEXT_PERIOD CONTRACT
-- ---------- ---------- ----------- --------
-- 111 202204 202205 1NA
-- 111 202205 202206 1NA
-- 111 202206 202309 1NA
-- 112 202207 202208 1NA
-- 112 202208 202207 1NA
It is pretty much the same - just showing next period instead of previous.
NOTE: If rehire to a contract means the same contract then -
OVER(Partition By EMP_ID, CONTRACT ....)
To do the opposite (non-consecutive periods):
SELECT EMP_ID, PERIOD "PERIOD", NEXT_PERIOD "NEXT_PERIOD", CONTRACT
FROM (Select EMP_ID, PERIOD, CONTRACT,
LEAD(PERIOD, 1, 0) OVER(Partition By EMP_ID Order By PERIOD) "NEXT_PERIOD"
From contracts)
WHERE NEXT_PERIOD != 0 And CASE WHEN SubStr(NEXT_PERIOD, 1, 4) = SubStr(PERIOD, 1, 4)
THEN NEXT_PERIOD - PERIOD
ELSE NEXT_PERIOD - (PERIOD 88) -- handling the year change
END > 1
--
-- R e s u l t :
-- EMP_ID PERIOD NEXT_PERIOD CONTRACT
-- ---------- ---------- ----------- --------
-- 111 202206 202209 1NA