Reputation: 359
I need to find what the date is 7 days ahead of any given date. The start date can be any date, however, the end date should not be a Sunday or a day marked as a holiday. There can only ever be 2 consecutive holiday days.
I have code that works, however, it's very long-winded. Is there potentially a simpler solution?
Also, if the required date range changed at some point in the future or the possible number of consecutive holidays changed, the script would need to be updated in multiple places, if this could be reduced then that would be great.
I already have a table (CALENDAR) containing a substantial range of dates, their day of the week and whether the date is considered a holiday. Something like this:
START_DATE, DAY_OF_WEEK, HOLIDAY
10-DEC-17 , Sun , 0
11-DEC-17 , Mon , 0
12-DEC-17 , Tue , 0
13-DEC-17 , Wed , 0
14-DEC-17 , Thu , 0
15-DEC-17 , Fri , 0
16-DEC-17 , Sat , 0
17-DEC-17 , Sun , 0
18-DEC-17 , Mon , 0
19-DEC-17 , Tue , 0
20-DEC-17 , Wed , 0
21-DEC-17 , Thu , 0
22-DEC-17 , Fri , 0
23-DEC-17 , Sat , 0
24-DEC-17 , Sun , 0
25-DEC-17 , Mon , 1
26-DEC-17 , Tue , 1
27-DEC-17 , Wed , 0
28-DEC-17 , Thu , 0
29-DEC-17 , Fri , 0
30-DEC-17 , Sat , 0
31-DEC-17 , Sun , 0
01-JAN-18 , Mon , 1
etc...
The expected OUTPUT would be something like:
START_DATE, END_DATE
10-DEC-17, 18-DEC-17
11-DEC-17, 18-DEC-17
12-DEC-17, 19-DEC-17
13-DEC-17, 20-DEC-17
14-DEC-17, 21-DEC-17
15-DEC-17, 22-DEC-17
16-DEC-17, 23-DEC-17
17-DEC-17, 27-DEC-17
18-DEC-17, 27-DEC-17
19-DEC-17, 27-DEC-17
20-DEC-17, 27-DEC-17
21-DEC-17, 28-DEC-17
22-DEC-17, 29-DEC-17
23-DEC-17, 30-DEC-17
24-DEC-17, 02-JAN-18
25-DEC-17, 02-JAN-18
26-DEC-17, 02-JAN-18
27-DEC-17, 03-JAN-18
28-DEC-17, 04-JAN-18
etc...
Below is my existing code. My approach is that as there can only ever be 3 consecutive excluded days (a Sunday followed by 2 holiday days), then I check 4 days ahead for each date and then take the first one which is not an excluded date. One of the 4 should always be a valid end date.
with temp as
(
select
start_date,
case
when lead(day_of_week, 7) over(order by start_date) = 'Sun'
or lead(holiday, 7) over(order by start_date) = 1
then null
else
lead(start_date, 7) over(order by start_date)
end as days_7,
case
when lead(day_of_week, 8) over(order by start_date) = 'Sun'
or lead(holiday, 8) over(order by start_date) = 1
then null
else
lead(start_date, 8) over(order by start_date)
end as days_8,
case
when lead(day_of_week, 9) over(order by start_date) = 'Sun'
or lead(holiday, 9) over(order by start_date) = 1
then null
else
lead(start_date, 9) over(order by start_date)
end as days_9,
case
when lead(day_of_week, 10) over(order by start_date) = 'Sun'
or lead(holiday, 10) over(order by start_date) = 1
then null
else
lead(start_date, 10) over(order by start_date)
end as days_10
from
calendar
)
select
start_date,
COALESCE(days_7, days_8, days_9, days_10) as end_date
from
temp
Upvotes: 0
Views: 250
Reputation: 3841
given holidays table:
with holidays as (
select to_date('10-DEC-17','dd-MON-YY') as mydate , 'Sun' as myday, 0 holiday from dual
union all
select to_date('11-DEC-17','dd-MON-YY') as mydate , 'Mon' as myday, 0 holiday from dual
union all
select to_date('12-DEC-17','dd-MON-YY') as mydate , 'Tue' as myday, 0 holiday from dual
union all
select to_date('13-DEC-17','dd-MON-YY') as mydate , 'Wed' as myday, 0 holiday from dual
union all
select to_date('14-DEC-17','dd-MON-YY') as mydate , 'Thu' as myday, 0 holiday from dual
union all
select to_date('15-DEC-17','dd-MON-YY') as mydate , 'Fri' as myday, 0 holiday from dual
union all
select to_date('16-DEC-17','dd-MON-YY') as mydate , 'Sat' as myday, 0 holiday from dual
union all
select to_date('17-DEC-17','dd-MON-YY') as mydate , 'Sun' as myday, 0 holiday from dual
union all
select to_date('18-DEC-17','dd-MON-YY') as mydate , 'Mon' as myday, 0 holiday from dual
union all
select to_date('19-DEC-17','dd-MON-YY') as mydate , 'Tue' as myday, 0 holiday from dual
union all
select to_date('20-DEC-17','dd-MON-YY') as mydate , 'Wed' as myday, 0 holiday from dual
union all
select to_date('21-DEC-17','dd-MON-YY') as mydate , 'Thu' as myday, 0 holiday from dual
union all
select to_date('22-DEC-17','dd-MON-YY') as mydate , 'Fri' as myday, 0 holiday from dual
union all
select to_date('23-DEC-17','dd-MON-YY') as mydate , 'Sat' as myday, 0 holiday from dual
union all
select to_date('24-DEC-17','dd-MON-YY') as mydate , 'Sun' as myday, 0 holiday from dual
union all
select to_date('25-DEC-17','dd-MON-YY') as mydate , 'Mon' as myday, 1 holiday from dual
union all
select to_date('26-DEC-17','dd-MON-YY') as mydate , 'Tue' as myday, 1 holiday from dual
union all
select to_date('27-DEC-17','dd-MON-YY') as mydate , 'Wed' as myday, 0 holiday from dual
union all
select to_date('28-DEC-17','dd-MON-YY') as mydate , 'Thu' as myday, 0 holiday from dual
union all
select to_date('29-DEC-17','dd-MON-YY') as mydate , 'Fri' as myday, 0 holiday from dual
union all
select to_date('30-DEC-17','dd-MON-YY') as mydate , 'Sat' as myday, 0 holiday from dual
union all
select to_date('31-DEC-17','dd-MON-YY') as mydate , 'Sun' as myday, 0 holiday from dual
union all
select to_date('01-JAN-18','dd-MON-YY') as mydate , 'Mon' as myday, 1 holiday from dual),
Here I find working days and then use first row to get the desired date
working as (
select holidays.*, case when myday='Sun' or holiday=1
then 0 else 1 end as working_day from holidays
)
select mydate from (
select mydate from working where
mydate>=to_date('21/12/2017','dd/mm/rrrr')+7
order by (case when working_day=0 then null else mydate end) asc nulls last
) where rownum=1
Upvotes: 0
Reputation: 168281
Oracle 11g R2 Schema Setup:
CREATE TABLE Calendar( START_DATE, HOLIDAY ) AS
SELECT TO_DATE( '10-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '11-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '12-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '13-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '14-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '15-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '16-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '17-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '18-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '19-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '20-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '21-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '22-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '23-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '24-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '25-DEC-17', 'DD-MON-YY' ), 1 FROM DUAL UNION ALL
SELECT TO_DATE( '26-DEC-17', 'DD-MON-YY' ), 1 FROM DUAL UNION ALL
SELECT TO_DATE( '27-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '28-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '29-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '30-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '31-DEC-17', 'DD-MON-YY' ), 0 FROM DUAL UNION ALL
SELECT TO_DATE( '01-JAN-18', 'DD-MON-YY' ), 1 FROM DUAL;
Query 1:
SELECT start_date,
LEAD( end_date, 7 ) OVER ( ORDER BY start_date ) AS end_date
FROM (
SELECT start_date,
LAST_VALUE(
CASE
WHEN Holiday = 0
AND start_date - TRUNC( start_date, 'IW' ) < 6
THEN start_date
END
) IGNORE NULLS OVER ( ORDER BY start_date DESC ) AS End_date
FROM Calendar
)
| START_DATE | END_DATE |
|----------------------|----------------------|
| 2017-12-10T00:00:00Z | 2017-12-18T00:00:00Z |
| 2017-12-11T00:00:00Z | 2017-12-18T00:00:00Z |
| 2017-12-12T00:00:00Z | 2017-12-19T00:00:00Z |
| 2017-12-13T00:00:00Z | 2017-12-20T00:00:00Z |
| 2017-12-14T00:00:00Z | 2017-12-21T00:00:00Z |
| 2017-12-15T00:00:00Z | 2017-12-22T00:00:00Z |
| 2017-12-16T00:00:00Z | 2017-12-23T00:00:00Z |
| 2017-12-17T00:00:00Z | 2017-12-27T00:00:00Z |
| 2017-12-18T00:00:00Z | 2017-12-27T00:00:00Z |
| 2017-12-19T00:00:00Z | 2017-12-27T00:00:00Z |
| 2017-12-20T00:00:00Z | 2017-12-27T00:00:00Z |
| 2017-12-21T00:00:00Z | 2017-12-28T00:00:00Z |
| 2017-12-22T00:00:00Z | 2017-12-29T00:00:00Z |
| 2017-12-23T00:00:00Z | 2017-12-30T00:00:00Z |
| 2017-12-24T00:00:00Z | (null) |
| 2017-12-25T00:00:00Z | (null) |
| 2017-12-26T00:00:00Z | (null) |
| 2017-12-27T00:00:00Z | (null) |
| 2017-12-28T00:00:00Z | (null) |
| 2017-12-29T00:00:00Z | (null) |
| 2017-12-30T00:00:00Z | (null) |
| 2017-12-31T00:00:00Z | (null) |
| 2018-01-01T00:00:00Z | (null) |
Upvotes: 2