【问题标题】:create custom function for date difference excluding weekends and holidays in oracle sql在oracle sql中为不包括周末和节假日的日期差异创建自定义函数
【发布时间】:2021-07-17 14:15:13
【问题描述】:

我需要通过使用Oracle中的自定义函数将两个日期之间的天数计算为十进制不包括周末和节假日 SQL。 网站上有类似的问题;但是,正如我所看到的,它们都没有使用自定义函数要求以十进制形式输出。我需要小数的原因是之后能够使用/提取时间分量。如果已经有这样的问题,请分享链接。

借助我在互联网上找到的附加内容thanks to the author,尝试编写如下函数。内部子查询单独工作正常,但不能作为一个整体函数工作。

简而言之,这个想法是:

(计算 startdate 和 enddate 之间的天差)->(排除 startdate 和 enddate 之间的周末天数)->(排除 startdate 和 enddate 之间的周末天数)

当我尝试保存函数时,它给出了错误PLS-00103: Encountered the symbol "end-of-file"。由于我已经是函数新手,可能缺少一些基本的东西。

最后,如果您对如何提高代码效率有任何建议,请告诉我。

提前致谢!

CREATE OR REPLACE FUNCTION  NET_WORKING_DAYS (startdate IN DATE, enddate IN DATE)
RETURN NUMBER IS 
  WORKINGDAYS_BETWEEN NUMBER;
BEGIN

SELECT

    -- number of days between startdate and enddate
      (
        SELECT (TO_DATE('20160831150000','YYYYMMDDHH24MISS')  - TO_DATE('20160801000000','YYYYMMDDHH24MISS') ) DAYS_BETWEEN 
        FROM DUAL
      )  
    -
    -- number of weekend days (after a given date)
      (
        SELECT COUNT(1)  WEEKEND_DAYS_BETWEEN
        FROM
        (
          SELECT
            TO_DATE('20160701000000','YYYYMMDDHH24MISS') + SEQ as day_date,     --2016/07/01 is a constant/given date for this formula
            TO_CHAR(TO_DATE('20160701000000','YYYYMMDDHH24MISS') + SEQ , 'D') day_of_week
          FROM
            (
                SELECT ROWNUM-1 SEQ
                FROM   ( SELECT 1 FROM  DUAL CONNECT BY LEVEL<= 365 * 5) --5 years
            )
          ORDER BY 1
        )
        WHERE day_of_week IN (6,7) 
          AND day_date > TO_DATE('20160801000000','YYYYMMDDHH24MISS')     --this should be replaced with startdate parameter
          AND day_date < TO_DATE('20160831000000','YYYYMMDDHH24MISS')      --this should be replaced with enddate parameter
      )  
    -
    -- number of holidays (after a given date)
      (
        SELECT COUNT(1)
        FROM HOLIDAYS
        WHERE HOLIDAY_DATE > TO_DATE('20160801000000','YYYYMMDDHH24MISS')     --this should be replaced with startdate parameter
          AND HOLIDAY_DATE < TO_DATE('20160831000000','YYYYMMDDHH24MISS')      --this should be replaced with enddate parameter
      )
INTO WORKINGDAYS_BETWEEN
FROM DUAL;

RETURN WORKINGDAYS_BETWEEN;
END NET_WORKING_DAYS;

**EDIT-1:假期已在数据库的 HOLIDAYS 表中定义,对于此日期范围从 201608010000002016083100000030.06.2016 是假期日期。

【问题讨论】:

  • 对于 2016-08-26 08:16:39 到 2016-08-30 08:17:01 的范围,如何获得所需的解决方案 4.00025...天? 2016-08-27 是星期六,2016-08-28 是星期日,2016-08-30 是假期。因此,您应该在 2016-08-29 和 2016-08-26 08:16:39 到 2016-08-27 00:00:00 有 1 全天,总共 1.655 个工作日为 0.655 天。

标签: sql oracle


【解决方案1】:

您无需使用行生成器来枚举每天的天数 - 可以使用简单的计算来完成:

来自my answer here

CREATE FUNCTION getWorkingDays (
  in_start_date IN  DATE,
  in_end_date   IN  DATE
) RETURN NUMBER DETERMINISTIC
IS
  p_start_date   DATE;
  p_end_date     DATE;
  p_working_days NUMBER;
  p_holiday_days NUMBER;
BEGIN
  IF in_start_date IS NULL OR in_end_date IS NULL THEN
    RETURN NUll;
  END IF;

  p_start_date := LEAST( in_start_date, in_end_date );
  p_end_date   := GREATEST( in_start_date, in_end_date );

  -- 5/7 * ( Number of days between monday of the week containing the start date
  --         and monday of the week containing the end date )
  -- + LEAST( day of week for end date, 5 )
  -- - LEAST( day of week for start date, 5 )
  p_working_days := ( TRUNC( p_end_date, 'IW' ) - TRUNC( p_start_date, 'IW' ) ) * 5 / 7
                    + LEAST( p_end_date - TRUNC( p_end_date, 'IW' ), 5 )
                    - LEAST( p_start_date - TRUNC( p_start_date, 'IW' ), 5 );

  SELECT COALESCE(
           SUM(
             LEAST( p_end_date, holiday_date + INTERVAL '1' DAY )
             - GREATEST( p_start_date, holiday_date )
           ),
           0
         )
  INTO   p_holiday_days
  FROM   Holidays
  WHERE  HOLIDAY_DATE BETWEEN TRUNC( p_start_date )
                      AND     TRUNC( p_end_date )
  AND    HOLIDAY_DATE - TRUNC( HOLIDAY_DATE, 'IW' ) < 5;

  RETURN GREATEST( p_working_days - p_holiday_days, 0 );
END;
/

【讨论】:

  • 感谢您的回复。当我运行你分享的代码时,它只接受这部分CREATE FUNCTION getWorkingDays ( in_start_date IN DATE, in_end_date IN DATE ) RETURN NUMBER IS p_start_date DATE这可能是什么原因?
  • @kzmlbyrk 如果您在 SQL 开发人员中运行它,则将其作为脚本运行(使用 F5),而不是尝试将其作为语句运行(Ctrl-Enter)。
  • 我认为如果Holidays 表包含任何周末(例如劳动节,始终是 5 月 1 日可能会落在周六/周日),我认为结果将是错误的,即它会减去两倍。跨度>
  • @WernfriedDomscheit 更新添加了一个简单的过滤器来删除周末假期。
  • @MT0,我尝试过使用 F5 并尝试在函数本身的“代码”选项卡内对其进行编辑,但没有奏效:(还有其他建议吗?
猜你喜欢
  • 1970-01-01
  • 2012-05-03
  • 1970-01-01
  • 2012-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多