I think the approach to this is to use analytic functions ...
I installed your test case using:
create table employee_job ( emp_id integer, job_id integer, status varchar2(1 char), eff_date date ); insert into employee_job values (1,10,'A',to_date('10-JAN-2008','DD-MON-YYYY')); insert into employee_job values (2,11,'A',to_date('13-JAN-2008','DD-MON-YYYY')); insert into employee_job values (1,12,'A',to_date('20-JAN-2008','DD-MON-YYYY')); insert into employee_job values (2,11,'T',to_date('01-FEB-2008','DD-MON-YYYY')); insert into employee_job values (1,10,'T',to_date('02-FEB-2008','DD-MON-YYYY')); insert into employee_job values (2,11,'A',to_date('20-FEB-2008','DD-MON-YYYY')); commit;
I used the lead function to get the next date, and then wrapped it as a subquery to get the “A” records and add the end date, if any.
select emp_id, job_id, eff_date start_date, decode(next_status,'T',next_eff_date,null) end_date from ( select emp_id, job_id, eff_date, status, lead(eff_date,1,null) over (partition by emp_id, job_id order by eff_date, status) next_eff_date, lead(status,1,null) over (partition by emp_id, job_id order by eff_date, status) next_status from employee_job ) where status = 'A' order by start_date, emp_id, job_id
I am sure that I missed some use cases, but you understood this idea. Analytical functions are your friend :)
EMP_ID JOB_ID START_DATE END_DATE 1 10 10-JAN-2008 02-FEB-2008 2 11 13-JAN-2008 01-FEB-2008 2 11 20-FEB-2008 1 12 20-JAN-2008