Reputation: 31
I have a table with a date from 02/11/2015 to 01/12/2015. eg...
ATTNDATE
--------
02/11/2015
03/11/2015
--
--
--
01/12/2015.
This table may have some missing dates also.. Lets assume 06/11/2015 and 20/11/2015 is missing from this table.
I want to get an output like
SL.No ATTNFROM ATTNTILL
1. 02/11/2015 05/11/2015
2. 07/11/2015 19/11/2015
3. 21/11/2015 01/12/2015
Kindly help me to get this output in oracle plsql
Upvotes: 3
Views: 172
Reputation: 191560
You can do this with lead and lag analytic functions - in a subquery which you then group over, which may be what you missed - but you can also do it with an analytic 'trick'.
If you look at the difference between each date and the lowest date you get a broken sequence, in your case 0, 1, 2, 3, 5, ..., 27, 28, 29. You can see that with attndate - min(attndate) over ()
.
You also have another unbroken sequence available from row_number() over (order by attndate)
, which gives you 1, 2, 3, ... 28.
If you subtract one from the other each contiguous block of dates gets the same answer, which I've called 'slot_no':
select attndate,
attndate - min(attndate) over ()
- row_number() over (order by attndate) as slot_no
from your_table;
With this data every row gets either -1, 0 or 1. (You can add two to those to make them more friendly if you want, but that only really works if the gaps in the data are single days). You can then group by that slot number:
with cte as (
select attndate,
attndate - min(attndate) over ()
- row_number() over (order by attndate) as slot_no
from your_table
)
select dense_rank() over (order by slot_no) as slot_no,
min(attndate) as attnfrom, max(attndate) as attntill
from cte
group by slot_no
order by slot_no;
With some generated data:
alter session set nls_date_format = 'DD/MM/YYYY';
with your_table (attndate) as (
select date '2015-11-02' + level - 1 from dual connect by level <= 4
union all select date '2015-11-07' + level - 1 from dual connect by level <= 13
union all select date '2015-11-21' + level - 1 from dual connect by level <= 11
),
cte as (
select attndate,
attndate - min(attndate) over ()
- row_number() over (order by attndate) as slot_no
from your_table
)
select dense_rank() over (order by slot_no) as slot_no,
min(attndate) as attnfrom, max(attndate) as attntill
from cte
group by slot_no
order by slot_no;
SLOT_NO ATTNFROM ATTNTILL
---------- ---------- ----------
1 02/11/2015 05/11/2015
2 07/11/2015 19/11/2015
3 21/11/2015 01/12/2015
If your real scenario is getting these ranges for multiple keys, say a person ID, then you can add a partition by
clause to each of the analytic function calls, in the three over ()
sections.
Upvotes: 6