SSA_Tech124
SSA_Tech124

Reputation: 599

Query based on several conditions for status_type

I want to fetch the latest ACTIVE STATUS TYPE WITH ACTION_CODE HIRE OR IF THE STATUS_TYPE HAS CHANGED FROM INACTIVE TO ACTIVE, then the query should fetch the first record just after the INACTIVE status.

for example, shown below

asg_table :

employee_number          start_Date        END_DATE                action_code            status_type  
1                       01-JAN-2019        02-JUL-2019               HIRE                   ACTIVE  
1                       03-JUL-2019        06-AUG-2019               ASG_CHANGE             ACTIVE
1                       07-AUG-2019        31-DEC-4712               ASG_CHANGE             ACTIVE


employee_number          start_Date        END_DATE                action_code            status_type  
2                       01-JAN-2019        02-JUL-2019               HIRE                   ACTIVE  
2                       03-JUN-2019        05-JUL-2019               TERM                   INACTIVE
2                       07-AUG-2019        15-AUG-2019               ASG_CHANGE             ACTIVE
2                       16-AUG-2019        31-DEC-4712               ASG_CHANGE             ACTIVE




employee_number          start_Date        END_DATE                action_code            status_type  
3                       01-JAN-2019        02-JUL-2019               HIRE                   ACTIVE  
3                       03-JUN-2019        05-JUL-2019               GLB_TRANSFER          INACTIVE
3                       07-AUG-2019        15-AUG-2019               GLB_TRANSFER             ACTIVE
3                       16-AUG-2019        31-DEC-4712               ASG_CHANGE             ACTIVE


employee_number          start_Date        END_DATE                action_code            status_type  
4                       01-JAN-2019        02-JUL-2019               HIRE                   ACTIVE  
4                       03-JUL-2019        06-AUG-2019               ASG_CHANGE             ACTIVE
4                       07-AUG-2019        31-DEC-4712               TERM                   INACTIVE

What approach can I follow to include all the conditions in one query.

Upvotes: 1

Views: 143

Answers (4)

Belayer
Belayer

Reputation: 14934

The issue is that you require two inflection points from your data: the maximum end date for status Inactive (or an assigned one where there is none) and minimum start date following that event. The following manages both those points.
I do not claim this is the most performant method not the most elegant. However, it is clear and easily followed showing the development of each step.

create table employee_status_hist ( employee_number integer
                                  , start_date      date
                                  , end_date        date
                                  , action_code     varchar2(16)
                                  , status_type     varchar2(08)
                                  );
insert into employee_status_hist  (employee_number,start_date,end_date,action_code,status_type)                                                                                                                                    
    select 1, to_date ('01-JAN-2019','dd-mon-yyyy'), to_date('02-JUL-2019','dd-mon-yyyy'), trim('HIRE      '), trim('ACTIVE  ') from dual union all                                                            
    select 1, to_date ('03-JUL-2019','dd-mon-yyyy'), to_date('06-AUG-2019','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 1, to_date ('07-AUG-2019','dd-mon-yyyy'), to_date('31-DEC-4712','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 2, to_date ('01-JAN-2019','dd-mon-yyyy'), to_date('02-JUL-2019','dd-mon-yyyy'), trim('HIRE      '), trim('ACTIVE  ') from dual union all                                                            
    select 2, to_date ('03-JUN-2019','dd-mon-yyyy'), to_date('05-JUL-2019','dd-mon-yyyy'), trim('TERM      '), trim('INACTIVE') from dual union all                                                            
    select 2, to_date ('07-AUG-2019','dd-mon-yyyy'), to_date('15-AUG-2019','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 2, to_date ('16-AUG-2019','dd-mon-yyyy'), to_date('31-DEC-4712','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 3, to_date ('01-JAN-2019','dd-mon-yyyy'), to_date('02-JUL-2019','dd-mon-yyyy'), trim('HIRE      '), trim('ACTIVE  ') from dual union all                                                            
    select 3, to_date ('03-JUN-2019','dd-mon-yyyy'), to_date('05-JUL-2019','dd-mon-yyyy'), trim('GLB_TRANSF'), trim('INACTIVE') from dual union all                                                            
    select 3, to_date ('07-AUG-2019','dd-mon-yyyy'), to_date('15-AUG-2019','dd-mon-yyyy'), trim('GLB_TRANSF'), trim('ACTIVE  ') from dual union all                                                            
    select 3, to_date ('16-AUG-2019','dd-mon-yyyy'), to_date('31-DEC-4712','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 4, to_date ('01-JAN-2019','dd-mon-yyyy'), to_date('02-JUL-2019','dd-mon-yyyy'), trim('HIRE      '), trim('ACTIVE  ') from dual union all                                                            
    select 4, to_date ('03-JUL-2019','dd-mon-yyyy'), to_date('06-AUG-2019','dd-mon-yyyy'), trim('ASG_CHANGE'), trim('ACTIVE  ') from dual union all                                                            
    select 4, to_date ('07-AUG-2019','dd-mon-yyyy'), to_date('31-DEC-4712','dd-mon-yyyy'), trim('TERM      '), trim('INACTIVE') from dual ;                                                                    

with last_inactive as 
     ( select esh.employee_number, max(esh.end_date) end_date 
         from employee_status_hist esh
        where status_type = 'INACTIVE'
        group by  esh.employee_number
       union all
       select distinct esh.employee_number, to_date('01-jan-0001','dd-mon-yyyy') end_date
         from employee_status_hist esh
        where not exists ( select null 
                             from employee_status_hist esh2
                            where esh.employee_number = esh2.employee_number
                              and status_type = 'INACTIVE'
                         )
      )                       
   , desired as 
     ( select esh.employee_number, min(esh.start_date) start_date
         from employee_status_hist esh
         join last_inactive        lin
           on (    esh.employee_number = lin.employee_number
               and esh.start_date      > lin.end_date
              )
        group by esh.employee_number
     )  
select employee_number, start_date,  action_code  
  from employee_status_hist
 where (employee_number, start_date) in 
       (select employee_number, start_date
          from desired
       );

Upvotes: 0

Rustam Pulatov
Rustam Pulatov

Reputation: 665

Use row_number(), like:

SELECT * 
FROM
(select mytbl.*,
 row_number() over (partition by mytbl.id order by <condition order> ) rn  
 from mytbl) a 
WHERE a.rn = 1

example:

https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=f5e43c0f001657f1bd17fb89186df6ef

with mytbl as  
(select 1  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 1  id, 
    TO_DATE('03-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('06-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 1  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('03-JUN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'TERM' action_code,
    'INACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('15-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('16-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('03-JUN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('05-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'GLB_TRANSFER' action_code,
    'INACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('15-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'GLB_TRANSFER' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('16-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual)
SELECT * 
FROM
(select mytbl.*,
 row_number() over (partition by mytbl.id 
            order by 
                case when mytbl.ACTION_CODE = 'GLB_TRANSFER' 
                        and mytbl.start_date = TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American')
                        and status_type = 'ACTIVE' then 0 
                        else 1  end) rn
 from mytbl) a 
WHERE a.rn = 1

It's true for 3th. I think you have general condition

OUTPUT:

1   01.01.2019  02.07.2019  HIRE            ACTIVE  1
2   01.01.2019  02.07.2019  HIRE            ACTIVE  1
3   07.08.2019  15.08.2019  GLB_TRANSFER    ACTIVE  1

also:

with mytbl as  
(select 1  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 1  id, 
    TO_DATE('03-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('06-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 1  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('03-JUN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'TERM' action_code,
    'INACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('15-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 2  id, 
    TO_DATE('16-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('01-JAN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('02-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('03-JUN-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('05-JUL-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'GLB_TRANSFER' action_code,
    'INACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('07-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('15-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'GLB_TRANSFER' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('16-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 3  id, 
    TO_DATE('16-AUG-2019', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'ASG_CHANGE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 4  id, 
    TO_DATE('01-AUG-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'HIRE' action_code,
    'ACTIVE' status_type FROM dual
union all
select 4  id, 
    TO_DATE('21-AUG-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'TRANSFER' action_code,
    'INACTIVE' status_type FROM dual
union all
select 4  id, 
    TO_DATE('21-AUG-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'TRANSFER' action_code,
    'ACTIVE' status_type FROM dual
union all
select 4  id, 
    TO_DATE('01-NOV-2018', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') start_date,  
    TO_DATE('31-DEC-4712', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE = American') end_date,
    'CHANGE_ASG' action_code,
    'ACTIVE' status_type FROM dual)
SELECT a.id, a.start_date, a.end_date, a.action_code, a.status_type
FROM
(SELECT a.id, a.start_date, a.end_date, a.action_code, a.status_type, 
row_number() over (partition by a.id order by start_date) rn
FROM  mytbl a
WHERE NOT EXISTS 
    (SELECT 1 
    FROM mytbl b
    WHERE b.status_type = 'INACTIVE'
    AND a.id = b.id)) a 
WHERE a.rn = 1
UNION ALL
SELECT a.id, a.start_date, a.end_date, a.action_code, a.status_type
FROM
(SELECT a.id, a.start_date, a.end_date, a.action_code, a.status_type, 
row_number() over (partition by a.id order by start_date) rn
FROM  mytbl a
WHERE EXISTS 
    (SELECT 1 
    FROM mytbl b
    WHERE b.status_type = 'INACTIVE'
    AND a.id = b.id
    AND a.start_date > b.start_date)) a 
WHERE a.rn = 1

Upvotes: 1

Popeye
Popeye

Reputation: 35920

You can achieve this using multiple subqueries and analytical function:

SELECT T.* FROM
 (SELECT T.*, 
         ROW_NUMBER() 
         OVER (partition by employee_number ORDER BY START_DATE)AS RN
         FROM
         (SELECT T.* 
            FROM
            (Select t.*, 
                    max(case when status_type='INACTIVE' THEN start_date END) 
                    over (partition by employee_number) AS MAX_INACTIVE_DATE
               FROM ASG_TABLE t) t
         WHERE COALESCE(MAX_INACTIVE_DATE, START_DATE) >= START_DATE
           AND STATUS_TYPE='ACTIVE') t ) t
WHERE RN = 1;

Cheers!!

Upvotes: 0

CompEng
CompEng

Reputation: 7386

try this: (this answer is for the initial version of the question you are asking.)

WITH DATASET AS (
SELECT 1 ASG_NUMBER,TO_DATE('20180101','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 1 ASG_NUMBER,TO_DATE('20180121','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 1 ASG_NUMBER,TO_DATE('20180219','yyyymmdd') EFFECTIVE_START_DATE,'INACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 1 ASG_NUMBER,TO_DATE('20180701','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 1 ASG_NUMBER,TO_DATE('20190101','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 2 ASG_NUMBER,TO_DATE('20190201','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 2 ASG_NUMBER,TO_DATE('20190402','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 3 ASG_NUMBER,TO_DATE('20190301','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 3 ASG_NUMBER,TO_DATE('20190402','yyyymmdd') EFFECTIVE_START_DATE,'INACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 4 ASG_NUMBER,TO_DATE('20180801','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 4 ASG_NUMBER,TO_DATE('20180821','yyyymmdd') EFFECTIVE_START_DATE,'INACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 4 ASG_NUMBER,TO_DATE('20180821','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL UNION ALL
SELECT 4 ASG_NUMBER,TO_DATE('20181101','yyyymmdd') EFFECTIVE_START_DATE,'ACTIVE'STATUS_TYPE FROM DUAL  
), SUB1 AS(
SELECT
ASG_NUMBER,
MAX (STATUS_TYPE) KEEP (DENSE_RANK FIRST ORDER BY EFFECTIVE_START_DATE DESC) STATUS_TYPE,
MAX (CASE WHEN STATUS_TYPE ='INACTIVE' THEN EFFECTIVE_START_DATE ELSE NULL END)INACTIVE_DATE
FROM DATASET
GROUP BY ASG_NUMBER
),INACTIVE_2_ACTIVE AS (
SELECT
 DATASET.ASG_NUMBER,
 MIN(EFFECTIVE_START_DATE) EFFECTIVE_START_DATE
FROM DATASET,SUB1
WHERE 1=1
AND DATASET.ASG_NUMBER=SUB1.ASG_NUMBER
AND DATASET.EFFECTIVE_START_DATE>=SUB1.INACTIVE_DATE
AND DATASET.STATUS_TYPE='ACTIVE'
GROUP BY DATASET.ASG_NUMBER
),JUST_ACTIVE AS (
SELECT 
 DATASET.ASG_NUMBER,
 MIN(EFFECTIVE_START_DATE) EFFECTIVE_START_DATE
FROM
DATASET,SUB1
WHERE 1=1
AND DATASET.ASG_NUMBER=SUB1.ASG_NUMBER
AND SUB1.INACTIVE_DATE IS NULL
AND DATASET.STATUS_TYPE='ACTIVE'
GROUP BY DATASET.ASG_NUMBER
),LASTVALUE_INACTIVE AS 
(
SELECT 
 DATASET.ASG_NUMBER,
 MIN(EFFECTIVE_START_DATE) EFFECTIVE_START_DATE
FROM
DATASET,SUB1
WHERE 1=1
AND DATASET.ASG_NUMBER=SUB1.ASG_NUMBER
AND DATASET.EFFECTIVE_START_DATE<=SUB1.INACTIVE_DATE 
AND DATASET.STATUS_TYPE='ACTIVE'
AND SUB1.STATUS_TYPE='INACTIVE'
GROUP BY DATASET.ASG_NUMBER
)
SELECT *
 FROM
 INACTIVE_2_ACTIVE
UNION 
SELECT *
 FROM
 JUST_ACTIVE
UNION 
SELECT *
 FROM
LASTVALUE_INACTIVE

Upvotes: 0

Related Questions