2012-10-17 41 views
1

我需要編寫一個sql查詢,返回兩個給定日期之間的工作日數(週一至週五)。SQL返回2個傳入日期之間的工作天數

我想知道什麼是最有效的方法來做到這一點?

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 

那麼它也將有助於能夠從這個計數中刪除假期,如聖誕節和節禮日。

任何想法?

+0

要刪除的假期,你需要在假期加盟日曆,特定於您要描述的語言環境。 – gorzan

+0

我認爲我需要這樣做,但在計算工作日方面呢?例如,如果它已經過去了幾年,那麼它是否有效並且效率如何? – Mac

+0

去檢查[Simlier問題](http://stackoverflow.com/questions/10929608/how-to-get-14-days-prior-to-the-given-date-avoiding-holidays) – nnnn

回答

1

下面是一個例子

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') 

示範

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                  
+0

對不起,downvote沒有必要。但它不以任何方式工作。 它可以做更多的解釋,也可以插入值來顯示它是如何工作的。我不明白。 – Mac

+1

我已經更新了答案 –

+1

您的RDBMS是Oracle還是SQL Server? –

3

這就是這麼簡單:

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. 
+0

如果ALL_OBJECTS表的這個技巧沒有足夠的行,該怎麼辦? ;)應該使用「正確」的方式來生成足夠多行的表格。此外,你的例子爲我返回33,而不是25. –

+0

...原因是NLS,只需爲每個TO_CHAR函數調用添加'NLS_DATE_LANGUAGE = english'(就像在其他答案中一樣)。 –

0

在這裏你去...

  1. 首先檢查你有多少天假期表中,不包括週末的日子。
  2. 獲取兩個日期之間的工作日(MON到FRI),然後減去假日。

    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; 
    

之後,你可以對其進行測試,如:

select 
      calculate_business_days('21-AUG-2013','28-AUG-2013') as business_days 
    from dual; 
0

這是我如何做到這一點,假設你已擁有將aColumn這表明,如果每天工作日曆表一天或不是: 向workday_num添加一列到您的日曆表中,並使用運行號填充一次

sum(case when workingday then 1 else 0 end) 
over (order by calendardate rows unbounded preceding) 

現在,它是您日曆的兩個連接,以及p_start_date和p_end_date的workday_nums的簡單區別。

0

它可以通過以下方式實現:

select SUM(decode (to_CHAR((sysdate-ROWNUM),'DY'),'SUN',0,'SAT',0,1)) from all_objects where rownum < sysdate - (sysdate -9) 
+0

只是代碼是不夠的。你應該提供一些細節和代碼。 – NightFury

3

一個簡單的方法來計算的工作日數2個日期之間是:

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 
+0

這很有效,除了最後乘以24以獲得某種工作時間概念。此外,週末的「開始計數」,即星期六 - >星期一爲1,但星期五 - >星期一也爲1. – Jeremy

+0

在'date1'大於'date2'時不能準確反映 –