在Oracle SQL中计算工作日(无函数或过程)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Oracle SQL中计算工作日(无函数或过程)相关的知识,希望对你有一定的参考价值。

我正在尝试计算Oracle select中两个日期之间的工作日。当我的计算给出给定日期的大多数结果时,我得到了这一点(我将它与excel中的NETWORKDAYS进行比较),但有时它在2天到-2天之间变化 - 我不知道为什么......

这是我的代码:

SELECT
((to_char(CompleteDate,'J') - to_char(InstallDate,'J'))+1) - (((to_char(CompleteDate,'WW')+ (52 * ((to_char(CompleteDate,'YYYY') - to_char(InstallDate,'YYYY'))))) - to_char(InstallDate,'WW'))*2) as BusinessDays
FROM TABLE

谢谢!

答案

解决方案,最后:

SELECT OrderNumber, InstallDate, CompleteDate,
  (TRUNC(CompleteDate) - TRUNC(InstallDate) ) +1 - 
  ((((TRUNC(CompleteDate,'D'))-(TRUNC(InstallDate,'D')))/7)*2) -
  (CASE WHEN TO_CHAR(InstallDate,'DY','nls_date_language=english')='SUN' THEN 1 ELSE 0 END) -
  (CASE WHEN TO_CHAR(CompleteDate,'DY','nls_date_language=english')='SAT' THEN 1 ELSE 0 END) as BusinessDays
FROM Orders
ORDER BY OrderNumber;

感谢您的所有回复!

另一答案

干得好...

  1. 首先检查您在假期表中获得的天数,不包括周末天数。
  2. 获取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;
另一答案

还有另一种更简单的方法,使用connect by和dual ...

with t as (select to_date('30-sep-2013') end_date, trunc(sysdate) start_date from dual)select count(1) from dual, t where to_char(t.start_date  + level, 'D') not in (1,7) connect by t.start_date + level <= t.end_date;

通过connect获取从start_date到end_date的所有日期。然后,您可以排除不需要的日期,只计算所需的日期。

另一答案

这将返回工作日:

(CompleteDate-InstallDate)-2*FLOOR((CompleteDate-InstallDate)/7)-
  DECODE(SIGN(TO_CHAR(CompleteDate,'D')-
    TO_CHAR(InstallDate,'D')),-1,2,0)+DECODE(TO_CHAR(CompleteDate,'D'),7,1,0)-
    DECODE(TO_CHAR(InstallDate,'D'),7,1,0) as BusinessDays,
另一答案

我考虑了上面讨论的所有不同的方法,并提出了一个简单的查询,它给出了两个日期之间一年中每个月的工作日数:

WITH test_data AS ( SELECT TO_DATE('01-JAN-14') AS start_date, TO_DATE('31-DEC-14') AS end_date
FROM dual ), all_dates AS (
SELECT td.start_date, td.end_date, td.start_date + LEVEL-1 as week_day FROM test_data td CONNECT BY td.start_date + LEVEL-1 <= td.end_date) SELECT TO_CHAR(week_day, 'MON'), COUNT(*)
FROM all_dates WHERE to_char(week_day, 'dy', 'nls_date_language=AMERICAN') NOT IN ('sun' , 'sat') GROUP BY TO_CHAR(week_day, 'MON');

请随意根据需要修改查询。

另一答案

试试这个:

with holidays as 
(
select d from (
select minDate + level -1 d
 from (select min(submitDate) minDate, max (completeDate) maxDate
 from t)
 connect by level <= maxDate - mindate + 1) 
 where to_char(d, 'dy', 'nls_date_language=AMERICAN') not in ('sun' , 'sat')
)
select t.OrderNo, t.submitDate, t.completeDate, count(*) businessDays
from t join holidays h on h.d between t.submitDate and t.completeDate
group by t.OrderNo, t.submitDate, t.completeDate
order by orderno

Here is a sqlfiddle demo

另一答案

我看到标记的最终解决方案总是不正确。假设,InstallDate是本月的第1天(如果是星期六),则CompleteDate是月份的第16天(如果是星期日)

在这种情况下,实际营业日是10,但标记的查询结果将给出答案为12.因此,我们也必须处理这种类型的案例,我使用过

(CASE WHEN TO_CHAR(InstallDate,'DY','nls_date_language=english')='SAT' AND TO_CHAR(CompleteDate,'DY','nls_date_language=english')='SUN' THEN 2 ELSE 0 END

行来处理它。

SELECT OrderNumber, InstallDate, CompleteDate,
(TRUNC(CompleteDate) - TRUNC(InstallDate) ) +1 - 
((((TRUNC(CompleteDate,'D'))-(TRUNC(InstallDate,'D')))/7)*2) -
(CASE WHEN TO_CHAR(InstallDate,'DY','nls_date_language=english')='SUN' THEN 1 ELSE 0 END) -
(CASE WHEN TO_CHAR(CompleteDate,'DY','nls_date_language=english')='SAT' THEN 1 ELSE 0 END) -
(CASE WHEN TO_CHAR(InstallDate,'DY','nls_date_language=english')='SAT' AND TO_CHAR(CompleteDate,'DY','nls_date_language=english')='SUN' THEN 2 ELSE 0 END)as BusinessDays
FROM Orders
ORDER BY OrderNumber;
另一答案

我将我的示例更改为更具可读性并返回总线计数。几天之间。我不知道为什么你需要'J'-朱利安格式。所需要的只是开始/安装和结束/完成日期。您将使用此日期在两个日期之间获得正确的天数。用你的日期替换我的日期,如果需要,添加NLS ...:

 SELECT Count(*) BusDaysBtwn
  FROM
  (
  SELECT TO_DATE('2013-02-18', 'YYYY-MM-DD') + LEVEL-1 InstallDate  -- MON or any other day 
       , TO_DATE('2013-02-25', 'YYYY-MM-DD') CompleteDate           -- MON or any other day
       , TO_CHAR(TO_DATE('2013-02-18', 'YYYY-MM-DD') + LEVEL-1, 'DY') InstallDay   -- day of week
    FROM dual 
  CONNECT BY LEVEL <= (TO_DATE('2013-02-25', 'YYYY-MM-DD') - TO_DATE('2013-02-18', 'YYYY-MM-DD')) -- end_date - start_date 
   )
   WHERE InstallDay NOT IN ('SAT', 'SUN')
  /

  SQL> 5
另一答案

已接受的解决方案非常接近但在某些情况下似乎是错误的(例如,2015年2月1日至2015年2月28日或2015年5月1日至2015年5月31日)。这是一个精致的版本......

  end_date-begin_date+1 /* total days */
  - TRUNC(2*(end_date-begin_date+1)/7) /* weekend days in whole weeks */
  - (CASE
      WHEN TO_CHAR(begin_date,'D') = 1 AND REMAINDER(end_date-begin_date+1,7) > 0 THEN 1
      WHEN TO_CHAR(begin_date,'D') = 8 - REMAINDER(end_date-begin_date+1,7) THEN 1
      WHEN TO_CHAR(begin_date,'D') > 8 - REMAINDER(end_date-begin_date+1,7) THEN 2
      ELSE 0
    END) /* weekend days in partial week */
  AS business_days

处理7(整周)倍数的部分是好的。但是,在考虑部分周部分时,它取决于星期偏移量和部分部分中的天数,根据以下矩阵...

   654321
1N 111111
2M 100000
3T 210000
4W 221000
5R 222100
6F 222210
7S 222221
另一答案

要删除星期日和星期六,你可以使用它

SELECT Base_DateDiff
     - (floor((Base_DateDiff + 0 + Start_WeekDay) / 7))
     - (floor((Base_DateDiff + 1 + Start_WeekDay) / 7))
FROM   (SELECT 1 + TRUNC(InstallDate) - TRUNC(InstallDate, 'IW') Start_WeekDay
             , CompleteDate - InstallDate + 1 Base_DateDiff
        FROM TABLE) a

Base_DateDiff计算两个日期之间的天数 (floor((Base_DateDiff + 0 + Start_WeekDay) / 7))计算星期日的数量 (floor((Base_DateDiff + 1 + Start_WeekDay) / 7))计算星期六的数量

1 + TRUNC(InstallDate) - TRUNC(InstallDate, 'IW')星期一获得1分,星期日获得7分

另一答案

此查询可用于从给定日期开始N天(仅限工作日)

例如,从2017-05-17返回15天:

select date_point, closest_saturday - (15 - offset + floor((15 - offset) / 6) * 2) from(
   select date_point,
          closest_saturday,
          (case
             when weekday_num > 1 then
              weekday_num - 2
             else
              0
           end) offset
    from (
           select  to_date('2017-05-17', 'yyyy-mm-dd') date_point,
                   to_date('2017-05-17', 'yyyy-mm-dd') - to_char(to_date('2017-05-17', 'yyyy-mm-dd'), 'D') closest_saturday,
                   to_char(to_date('2017-05-17', 'yyyy-mm-dd'), 'D') weekday_num
           from dual
          ))

一些简短的解释:假设我们想要从给定日期起N天后退 - 找到小于或等于给定日期的最接近的星期六。 - 从最近的星期六开始,回到病房(N - 偏移)

以上是关于在Oracle SQL中计算工作日(无函数或过程)的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE存储过程里可以声明过程和函数吗

SQL函数和存储过程的区别

在 Java 中调用 Oracle PL/SQL 中的过程或函数。返回结果集 false

PL/SQL Oracle 无提示错误

ORACLE 存储函数

oracle数据库之存储函数和过程