You Asked
Hi Tom,
I would like to get the starting consecutive date range and ending consecutive date range for each id.
Take for example the following table:
create table holiday (id int, dt date);
insert into holiday values(1, to_date(20041231, 'yyyymmdd'));
insert into holiday values(1, to_date(20050101, 'yyyymmdd'));
insert into holiday values(1, to_date(20050131, 'yyyymmdd'));
insert into holiday values(2, to_date(20050201, 'yyyymmdd'));
insert into holiday values(3, to_date(20050301, 'yyyymmdd'));
insert into holiday values(3, to_date(20050302, 'yyyymmdd'));
insert into holiday values(3, to_date(20050305, 'yyyymmdd'));
insert into holiday values(3, to_date(20050320, 'yyyymmdd'));
insert into holiday values(3, to_date(20050321, 'yyyymmdd'));
insert into holiday values(3, to_date(20050322, 'yyyymmdd'));
select * from holiday;
ID DT
---------- --------
1 20041231
1 20050101
1 20050131
2 20050201
3 20050301
3 20050302
3 20050305
3 20050320
3 20050321
3 20050322
10 rows selected.
The output I would like is:
ID FRSD FRED LRSD LRED
--------- -------- -------- -------- --------
1 20041231 20050101 20050131 20050131
2 20050201 20050201 20050201 20050201
3 20050301 20050302 20050320 20050322
Where,
FRSD = First Range Start Date
FRED = First Range End Date
LRSD = Last Range Start Date
LRED = Last Range End Date
Is this possible to do with one SQL using analytics? If not, I'm assuming it would be possible with PL/SQL.
Thanks,
-Peter
and Tom said...
ops$tkyte@ORA9IR2> select id,
2 min(case when mgrp1=1 then dt end) frsd,
3 max(case when mgrp1=1 then dt end) fred,
4 min(case when mgrp2=1 then dt end) lrsd,
5 max(case when mgrp2=1 then dt end) lred
6 from (
7 select id, dt,
8 max(grp1) over (partition by id order by dt) mgrp1,
9 max(grp2) over (partition by id order by dt desc) mgrp2
10 from (
11 select id, dt,
12 case when nvl(lag(dt) over (partition by id order by dt),dt) <> dt-1
13 then row_number() over (partition by id order by dt)
14 end grp1,
15 case when nvl(lag(dt) over (partition by id order by dt desc),dt) <> dt+1
16 then row_number() over (partition by id order by dt desc)
17 end grp2
18 from holiday
19 )
20 )
21 group by id
22 /
ID FRSD FRED LRSD LRED
---------- --------- --------- --------- ---------
1 31-DEC-04 01-JAN-05 31-JAN-05 31-JAN-05
2 01-FEB-05 01-FEB-05 01-FEB-05 01-FEB-05
3 01-MAR-05 02-MAR-05 20-MAR-05 22-MAR-05
is the final answer... What I did was first set up the "contigous groups":
ops$tkyte@ORA9IR2> select id, dt,
2 case when nvl(lag(dt) over (partition by id order by dt),dt) <> dt-1
3 then row_number() over (partition by id order by dt)
4 end grp1
5 from holiday
6 order by id, dt
7 /
ID DT GRP1
---------- --------- ----------
1 31-DEC-04 1
1 01-JAN-05
1 31-JAN-05 3
2 01-FEB-05 1
3 01-MAR-05 1
3 02-MAR-05
3 05-MAR-05 3
3 20-MAR-05 4
3 21-MAR-05
3 22-MAR-05
10 rows selected.
What we need to do is carry down the last non-null GRP1 value for each row and we have our first range, likewise:
ops$tkyte@ORA9IR2> select id, dt,
2 case when nvl(lag(dt) over (partition by id order by dt desc),dt) <> dt+1
3 then row_number() over (partition by id order by dt desc)
4 end grp2
5 from holiday
6 order by id, dt desc
7 /
ID DT GRP2
---------- --------- ----------
1 31-JAN-05 1
1 01-JAN-05 2
1 31-DEC-04
2 01-FEB-05 1
3 22-MAR-05 1
3 21-MAR-05
3 20-MAR-05
3 05-MAR-05 4
3 02-MAR-05 5
3 01-MAR-05
we have to "carry up" (desc sort) the last non-null. We can do that in 9i with max:
ops$tkyte@ORA9IR2> select id, dt,
2 max(grp1) over (partition by id order by dt) mgrp1,
3 max(grp2) over (partition by id order by dt desc) mgrp2
4 from (
5 select id, dt,
6 case when nvl(lag(dt) over (partition by id order by dt),dt) <> dt-1
7 then row_number() over (partition by id order by dt)
8 end grp1,
9 case when nvl(lag(dt) over (partition by id order by dt desc),dt) <> dt+1
10 then row_number() over (partition by id order by dt desc)
11 end grp2
12 from holiday
13 )
14 order by id, dt
15 /
ID DT MGRP1 MGRP2
---------- --------- ---------- ----------
1 31-DEC-04 1 2
1 01-JAN-05 1 2
1 31-JAN-05 3 1
2 01-FEB-05 1 1
3 01-MAR-05 1 5
3 02-MAR-05 1 5
3 05-MAR-05 3 4
3 20-MAR-05 4 1
3 21-MAR-05 4 1
3 22-MAR-05 4 1
10 rows selected.
Now, we have the first range with mgrp1 = 1 and the last range with mgrp2 = 1. We just min/max the dates for each of those groups by id and ... there you go :)
Rating
(3 ratings)
Is this answer out of date? If it is, please let us know via a Comment