Oracle SQL:查找指定日期前 7 天的日期,不包括星期日和其他特定日期
Posted
技术标签:
【中文标题】Oracle SQL:查找指定日期前 7 天的日期,不包括星期日和其他特定日期【英文标题】:Oracle SQL: Find the date 7 days ahead of a specified date excluding Sundays and other specific dates 【发布时间】:2018-02-21 10:19:07 【问题描述】:我需要找出比任何给定日期早 7 天的日期。开始日期可以是任何日期,但结束日期不应是星期日或标记为假日的日子。节假日只能连续 2 天。
我的代码可以运行,但是很冗长。是否有更简单的解决方案?
另外,如果要求的日期范围在未来某个时间点发生变化或可能的连续假期数发生变化,则脚本需要在多个地方更新,如果可以减少,那就太好了。
我已经有一个表格 (CALENDAR),其中包含大量日期、星期几以及该日期是否被视为假日。像这样的:
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...
预期的输出将类似于:
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...
以下是我现有的代码。我的方法是,由于只能连续排除 3 天(一个星期天,然后是 2 个假期),因此我会提前 4 天检查每个日期,然后选择第一个不是排除日期的日期。 4 个中的一个应始终是有效的结束日期。
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
【问题讨论】:
为此目的创建一个函数不是更直接吗? 【参考方案1】:SQL Fiddle
Oracle 11g R2 架构设置:
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;
查询 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
)
Results:
| 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) |
【讨论】:
很好的解决方案,非常感谢。不过有一件事,我需要将 IGNORE NULLS 移动到 LAST_VALUE 函数的第一组括号内,以使其运行。否则,它会抛出“ORA-30484:缺少此函数的窗口规范”。看起来 LAST_VALUE 函数的语法在 Oracle 10 和 11 之间发生了变化。【参考方案2】:给定假期表:
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),
在这里我找到工作日,然后使用第一行来获取所需的日期
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
【讨论】:
以上是关于Oracle SQL:查找指定日期前 7 天的日期,不包括星期日和其他特定日期的主要内容,如果未能解决你的问题,请参考以下文章