I have following initial dataset
F_ID L_CAT CHG_DT
F1 VHL 01-FEB-2016
F1 VHL 10-FEB-2016
F1 VHL 15-FEB-2016
F1 MHL 20-FEB-2016
F1 VHL 25-FEB-2016
F1 VHL 28-FEB-2016
F1 MHL 05-MAR-2016
F1 MHL 10-MAR-2016
F2 VHL 01-FEB-2016
F2 VHL 10-FEB-2016
F2 MHL 18-FEB-2016
F2 MHL 21-FEB-2016
F2 VHL 25-FEB-2016
and I want to generate following output from SQL Query:
F_ID L_CAT FROM_DT TO_DT
F1 VHL 01-FEB-2016 20-FEB-2016
F1 MHL 20-FEB-2016 25-FEB-2016
F1 VHL 25-FEB-2016 05-MAR-2016
F1 MHL 05-MAR-2016 10-MAR-2016
F2 VHL 01-FEB-2016 18-FEB-2016
F2 MHL 18-FEB-2016 25-FEB-2016
F2 VHL 25-FEB-2016 25-FEB-2016
i.e., I want to calculate time span during which each F_ID remain in specific L_CAT
code to produce scenario
create table my_test
(
f_id varchar2(30),
l_cat varchar2(30),
chg_dt date
);
insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','01-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','10-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','15-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','20-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','25-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','28-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','05-MAR-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','10-MAR-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','01-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','10-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F2','MHL','18-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F2','MHL','21-FEB-2016');
insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','25-FEB-2016');
COMMIT;
Thanks
There are more concise ways of doing this but I'll give you the longer version because it will let you build up the concepts here
SQL> @drop my_Test
Y1 Y2
-------------------------------------------------------------------------------------------- -------------------------
TABLE cascade constraints purge
1 row selected.
Table dropped.
SQL>
SQL> create table my_test
2 (
3 f_id varchar2(30),
4 l_cat varchar2(30),
5 chg_dt date
6 );
Table created.
SQL>
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','01-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','10-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','15-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','20-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','25-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','VHL','28-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','05-MAR-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F1','MHL','10-MAR-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','01-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','10-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F2','MHL','18-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F2','MHL','21-FEB-2016');
1 row created.
SQL> insert into my_test(f_id, l_cat, chg_dt) values ('F2','VHL','25-FEB-2016');
1 row created.
SQL> COMMIT;
Commit complete.
--
-- extract some information about previous and next row from the current row
--
SQL>
SQL>
SQL> select f_id,
2 l_cat,
3 chg_dt,
4 lead(chg_dt) over ( partition by f_id order by chg_dt ) nxt_dt,
5 lag(chg_dt) over ( partition by f_id order by chg_dt ) prev_dt,
6 lead(l_cat) over ( partition by f_id order by chg_dt ) nxt_cat,
7 lag(l_cat) over ( partition by f_id order by chg_dt ) prev_cat
8 from my_Test
9 order by 1,3;
F_ID L_CAT CHG_DT NXT_DT PREV_DT NXT_CAT PREV_CAT
------------------------------ ------------------------------ --------- --------- --------- ------------------------------ ------------------------------
F1 VHL 01-FEB-16 10-FEB-16 VHL
F1 VHL 10-FEB-16 15-FEB-16 01-FEB-16 VHL VHL
F1 VHL 15-FEB-16 20-FEB-16 10-FEB-16 MHL VHL
F1 MHL 20-FEB-16 25-FEB-16 15-FEB-16 VHL VHL
F1 VHL 25-FEB-16 28-FEB-16 20-FEB-16 VHL MHL
F1 VHL 28-FEB-16 05-MAR-16 25-FEB-16 MHL VHL
F1 MHL 05-MAR-16 10-MAR-16 28-FEB-16 MHL VHL
F1 MHL 10-MAR-16 05-MAR-16 MHL
F2 VHL 01-FEB-16 10-FEB-16 VHL
F2 VHL 10-FEB-16 18-FEB-16 01-FEB-16 MHL VHL
F2 MHL 18-FEB-16 21-FEB-16 10-FEB-16 MHL VHL
F2 MHL 21-FEB-16 25-FEB-16 18-FEB-16 VHL MHL
F2 VHL 25-FEB-16 21-FEB-16 MHL
13 rows selected.
-- DATE_FROM
-- when the prev is null (start of rows), we are at the start of a new range
-- or when this category is not the same as previous, we are at the start of a new range
--
-- DATE_TO
-- when the next is null (end of rows), we are at the end of current date range
-- or when next category is not same as this one, we are at the end of current date range
SQL> with t as
2 (
3 select f_id,
4 l_cat,
5 chg_dt,
6 lead(chg_dt) over ( partition by f_id order by chg_dt ) nxt_dt,
7 lag(chg_dt) over ( partition by f_id order by chg_dt ) prev_dt,
8 lead(l_cat) over ( partition by f_id order by chg_dt ) nxt_cat,
9 lag(l_cat) over ( partition by f_id order by chg_dt ) prev_cat
10 from my_Test
11 )
12 select
13 f_id,
14 l_cat,
15 case
16 when prev_dt is null then chg_dt
17 when l_cat != prev_cat then chg_dt
18 end date_from,
19 case
20 when nxt_dt is null then chg_dt
21 when l_cat != nxt_cat then nxt_dt
22 end date_to
23 from t;
F_ID L_CAT DATE_FROM DATE_TO
------------------------------ ------------------------------ --------- ---------
F1 VHL 01-FEB-16
F1 VHL
F1 VHL 20-FEB-16
F1 MHL 20-FEB-16 25-FEB-16
F1 VHL 25-FEB-16
F1 VHL 05-MAR-16
F1 MHL 05-MAR-16
F1 MHL 10-MAR-16
F2 VHL 01-FEB-16
F2 VHL 18-FEB-16
F2 MHL 18-FEB-16
F2 MHL 25-FEB-16
F2 VHL 25-FEB-16 25-FEB-16
13 rows selected.
--
-- now I want to carry the DATE_FROM down through every row
--
SQL> with t as
2 (
3 select f_id,
4 l_cat,
5 chg_dt,
6 lead(chg_dt) over ( partition by f_id order by chg_dt ) nxt_dt,
7 lag(chg_dt) over ( partition by f_id order by chg_dt ) prev_dt,
8 lead(l_cat) over ( partition by f_id order by chg_dt ) nxt_cat,
9 lag(l_cat) over ( partition by f_id order by chg_dt ) prev_cat
10 from my_Test
11 )
12 select
13 f_id,
14 l_cat,
15 chg_dt,
16 last_value(case
17 when prev_dt is null then chg_dt
18 when l_cat != prev_cat then chg_dt
19 end ignore nulls) over ( partition by f_id order by chg_dt) date_from,
20 case
21 when nxt_dt is null then chg_dt
22 when l_cat != nxt_cat then nxt_dt
23 end date_to
24 from t
25 /
F_ID L_CAT CHG_DT DATE_FROM DATE_TO
------------------------------ ------------------------------ --------- --------- ---------
F1 VHL 01-FEB-16 01-FEB-16
F1 VHL 10-FEB-16 01-FEB-16
F1 VHL 15-FEB-16 01-FEB-16 20-FEB-16
F1 MHL 20-FEB-16 20-FEB-16 25-FEB-16
F1 VHL 25-FEB-16 25-FEB-16
F1 VHL 28-FEB-16 25-FEB-16 05-MAR-16
F1 MHL 05-MAR-16 05-MAR-16
F1 MHL 10-MAR-16 05-MAR-16 10-MAR-16
F2 VHL 01-FEB-16 01-FEB-16
F2 VHL 10-FEB-16 01-FEB-16 18-FEB-16
F2 MHL 18-FEB-16 18-FEB-16
F2 MHL 21-FEB-16 18-FEB-16 25-FEB-16
F2 VHL 25-FEB-16 25-FEB-16 25-FEB-16
13 rows selected.
--
-- and with that, a simple GROUP BY / MAX will get the DATE_TO I need
--
SQL> with t as
2 (
3 select f_id,
4 l_cat,
5 chg_dt,
6 lead(chg_dt) over ( partition by f_id order by chg_dt ) nxt_dt,
7 lag(chg_dt) over ( partition by f_id order by chg_dt ) prev_dt,
8 lead(l_cat) over ( partition by f_id order by chg_dt ) nxt_cat,
9 lag(l_cat) over ( partition by f_id order by chg_dt ) prev_cat
10 from my_Test
11 ),
12 t2 as
13 (
14 select
15 f_id,
16 l_cat,
17 chg_dt,
18 last_value(case
19 when prev_dt is null then chg_dt
20 when l_cat != prev_cat then chg_dt
21 end ignore nulls) over ( partition by f_id order by chg_dt) date_from,
22 case
23 when nxt_dt is null then chg_dt
24 when l_cat != nxt_cat then nxt_dt
25 end date_to
26 from t
27 )
28 select
29 f_id,
30 l_cat,
31 date_from,
32 max(date_to)
33 from t2
34 group by f_id, l_cat, date_from
35 order by 1,3;
F_ID L_CAT DATE_FROM MAX(DATE_
------------------------------ ------------------------------ --------- ---------
F1 VHL 01-FEB-16 20-FEB-16
F1 MHL 20-FEB-16 25-FEB-16
F1 VHL 25-FEB-16 05-MAR-16
F1 MHL 05-MAR-16 10-MAR-16
F2 VHL 01-FEB-16 18-FEB-16
F2 MHL 18-FEB-16 25-FEB-16
F2 VHL 25-FEB-16 25-FEB-16
7 rows selected.
SQL>