问题
I need to write an sql query that returns the number of Working days (Monday - Friday) between two given dates.
I was wondering what would be the most efficient way to do this?
SELECT --Start with total number of days including weekends
(DATEDIFF(dd,@StartDate,@EndDate)+1) --Subtact 2 days for each full weekend
(DATEDIFF(wk,@StartDate,@EndDate)*2) --If StartDate is a Sunday, Subtract 1
ELSE 0 END) --If EndDate is a Saturday, Subtract 1
FROM dual
Then it would also be helpful to be able to remove holidays from this count such as christmas day and boxing day.
Any Ideas?
回答1:
Thats so simple :
SQL> Select count(*)
2 from ( select rownum rnum
3 from all_objects
4 where rownum <= to_date('18-dec-2009','dd-mon-yyyy') -
to_date('16-nov-2009')+1 )
5 where to_char( to_date('16-nov-2009','dd-mon-yyyy')+rnum-1, 'DY' )
6 not in ( 'SAT', 'SUN' )
COUNT(*)
----------
25
SQL> Select to_char( to_date('16-nov-2009','dd-mon-yyyy')+rnum-1, 'DY dd-mon-yyyy' )
2 from ( select rownum rnum
3 from all_objects
4 where rownum <= to_date('18-dec-2009','dd-mon-yyyy') - to_date('16-nov-2009')+1 )
5 where to_char( to_date('16-nov-2009','dd-mon-yyyy')+rnum-1, 'DY' )
6 not in ( 'SAT', 'SUN' )
DAY_DATE
---------------
MON 16-nov-2009
TUE 17-nov-2009
WED 18-nov-2009
THU 19-nov-2009
FRI 20-nov-2009
MON 23-nov-2009
TUE 24-nov-2009
WED 25-nov-2009
THU 26-nov-2009
FRI 27-nov-2009
MON 30-nov-2009
TUE 01-dec-2009
WED 02-dec-2009
THU 03-dec-2009
FRI 04-dec-2009
MON 07-dec-2009
TUE 08-dec-2009
WED 09-dec-2009
THU 10-dec-2009
FRI 11-dec-2009
MON 14-dec-2009
TUE 15-dec-2009
WED 16-dec-2009
THU 17-dec-2009
FRI 18-dec-2009
25 rows selected.
回答2:
an easy way to calculate to number of weekdays between 2 dates is :
SELECT
date1,
date2,
((date2-date1)-2*FLOOR((date2-date1)/7)-DECODE(SIGN(TO_CHAR(date2,'D')-
TO_CHAR(date1,'D')),-1,2,0)+DECODE(TO_CHAR(date1,'D'),7,1,0)-
DECODE(TO_CHAR(date2,'D'),7,1,0))*24 as WorkDays
FROM
tablename
ORDER BY date1,date2
回答3:
Here is an example
with given_days(d) as(
select <<start_date>> + level - 1
from dual
connect by level < = (<<end_date>> - <<start_date>>) + 1
)
select count(*)
from given_days
where to_char(d, 'DY', 'NLS_DATE_LANGUAGE=english') not in ('SUN', 'SAT')
Demonstration
HR\XE> with given_days as(
2 select (to_date('&&1', 'dd.mm.yyyy') + level - 1) as g_day
3 from dual
4 connect by level < = (to_date('&2', 'dd.mm.yyyy') - to_date('&&1', 'dd.mm.yyyy')) + 1
5 )
6 select count(g_day) as cnt
7 from given_days
8 where to_char(g_day, 'DY', 'NLS_DATE_LANGUAGE=english') not in ('SUN', 'SAT');
Enter value for 1: 10.10.2012
old 2: select to_date('&&1', 'dd.mm.yyyy') + level - 1
new 2: select to_date('10.10.2012', 'dd.mm.yyyy') + level - 1
Enter value for 2: 17.10.2012
old 4: connect by level < = (to_date('&2', 'dd.mm.yyyy') - to_date('&&1', 'dd.mm.yyyy')) + 1
new 4: connect by level < = (to_date('17.10.2012', 'dd.mm.yyyy') - to_date('10.10.2012', 'dd.mm.yyyy')) + 1
cnt
----------
6
回答4:
It can be achieved by:
select SUM(decode ( to_CHAR((sysdate-ROWNUM),'DY'),'SUN',0,'SAT',0,1)) from all_objects where rownum < sysdate - (sysdate -9)
回答5:
Here you go...
- First check how many days you got in the holiday table, excluding weekend days.
Get business days (MON to FRI) between the 2 dates and after that subtract the holiday days.
create or replace FUNCTION calculate_business_days (p_start_date IN DATE, p_end_date IN DATE) RETURN NUMBER IS v_holidays NUMBER; v_start_date DATE := TRUNC (p_start_date); v_end_date DATE := TRUNC (p_end_date); BEGIN IF v_end_date >= v_start_date THEN SELECT COUNT (*) INTO v_holidays FROM holidays WHERE day BETWEEN v_start_date AND v_end_date AND day NOT IN ( SELECT hol.day FROM holidays hol WHERE MOD(TO_CHAR(hol.day, 'J'), 7) + 1 IN (6, 7) ); RETURN GREATEST (NEXT_DAY (v_start_date, 'MON') - v_start_date - 2, 0) + ( ( NEXT_DAY (v_end_date, 'MON') - NEXT_DAY (v_start_date, 'MON') ) / 7 ) * 5 - GREATEST (NEXT_DAY (v_end_date, 'MON') - v_end_date - 3, 0) - v_holidays; ELSE RETURN NULL; END IF; END calculate_business_days;
After that you can test it out, like:
select
calculate_business_days('21-AUG-2013','28-AUG-2013') as business_days
from dual;
回答6:
This is how i do it, assuming you already got a calendar table with acolumn which indicates if a day is working day or not: Add a new column to your calendar table like workday_num and populate it once with a running number using
sum(case when workingday then 1 else 0 end)
over (order by calendardate rows unbounded preceding)
Now it's two joins to your calendar and a simple difference of the workday_nums of p_start_date and p_end_date.
来源:https://stackoverflow.com/questions/12932965/sql-to-return-the-number-of-working-days-between-2-passed-in-dates