【问题标题】:Oracle SQL for Last Business Day of Current Month including federal holidays in oracle当月最后一个工作日的 Oracle SQL,包括 oracle 中的联邦假日
【发布时间】:2018-10-23 15:38:24
【问题描述】:

我正在尝试查找用于查找当月最后一个工作日和上个月最后一个工作日的 Oracle SQL。这两种情况都应考虑联邦假日日历。

例如:

  • 当月最后一个工作日
  • 如果从 2019 年 11 月 15 日开始运行

从技术上讲,我应该在 11 月 29 日得到我的输出,因为 28 日是感恩节假期。

【问题讨论】:

标签: sql oracle


【解决方案1】:

将这些联邦假期存储在假期表中,为 DATE 类型,然后 尝试这样的操作:查找该月最后 7 天中最旧的 (MAX) 天,该天既不是星期六也不是星期日,也不是 假期表。

这里的假设是 1) 并非月末的所有 7 天都可以是节假日或周末,以及 2) 周六和周日休息。您可以相应地调整 level 或 where 子句,具体取决于上述假设是否应始终成立。

SELECT MAX(dt) AS last_working_day 
FROM
(
SELECT last_day(SYSDATE) - level + 1 as dt
FROM dual CONNECT BY
     level <= 7  -- the last seven days of the month
     )  WHERE TO_CHAR(dt,'DY', 'NLS_DATE_LANGUAGE = AMERICAN') NOT IN ('SAT','SUN')
     AND dt NOT IN ( SELECT holiday from federal_holidays );

更好的方法是创建一个日历表,其中包含一年中的所有日期和名为isbusinessday 的预定义列。那么查询就简单多了。

SELECT MAX(dt)
  FROM calendar
  WHERE isbusinessday = 'Y' 
AND TO_CHAR(dt,'YYYYMM') = TO_CHAR(SYSDATE,'YYYYMM');

【讨论】:

    【解决方案2】:

    一般来说,有一张假期表是可行的,但是由于某些假期正在移动,因此需要进行一些维护。例如,感恩节是 11 月的第四个星期四,即从 11 月 22 日到 11 月 28 日。

    您还可以使用 Oracle 内置调度程序。通常它用于控制SCHEDULER JOBS,但我看不出有什么理由不应该将它用于其他用途。

    首先创建一个联邦假日列表,例如:

    BEGIN
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'CHRISTMAS', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=1225', comments => 'December 25');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'COLUMBUS_DAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=OCT;BYDAY=2 MON', comments => '2nd Monday in October');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'INDEPENDENCE_DAY', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=0704', comments => 'July 4');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'MARTIN_LUTHER_KING_DAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=JAN;BYDAY=3 MON', comments => '3rd Monday in January');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'MEMORIAL_DAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=MAY;BYDAY=-1 MON', comments => 'Last Monday of May');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'NEW_YEARS_DAY', repeat_interval => 'FREQ=YEARLY;INTERVAL=1;BYDATE=0101', comments => 'January 1');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'THANKSGIVING', repeat_interval => 'FREQ=MONTHLY;BYMONTH=NOV;BYDAY=4 THU', comments => '4th Thursday in November');
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'WASHINGTONS_BIRTHDAY', repeat_interval => 'FREQ=MONTHLY;BYMONTH=FEB;BYDAY=3 MON', comments => '3rd Monday in February');
    
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'WEEKEND', repeat_interval => 'FREQ=DAILY;INTERVAL=1;BYDAY=SAT,SUN');
    
        -- Combined schedule for all federal holidays 
        DBMS_SCHEDULER.CREATE_SCHEDULE(schedule_name => 'FEDERAL_HOLIDAYS', repeat_interval => 'FREQ=DAILY;INTERSECT=CHRISTMAS,INDEPENDENCE_DAY,MARTIN_LUTHER_KING_DAY,MEMORIAL_DAY,NEW_YEARS_DAY,THANKSGIVING,WASHINGTONS_BIRTHDAY');
    
    END;
    /
    

    查看Calendaring Syntax,了解如何指定repeat_interval

    然后您可以使用程序DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING 获取您的日期:

    CREATE OR REPLACE FUNCTION LAST_BUSINESS_DAY(today IN TIMESTAMP DEFAULT SYSTIMESTAMP) RETURN TIMESTAMP AS 
        return_date_after TIMESTAMP := TRUNC(today);
        next_run_date TIMESTAMP;
    BEGIN
    
        LOOP
            DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERVAL=1;EXCLUDE=FEDERAL_HOLIDAYS,WEEKEND', NULL, return_date_after, next_run_date);
            EXIT WHEN next_run_date >= LAST_DAY(TRUNC(today));
            return_date_after := next_run_date;
        END LOOP;
    
        RETURN return_date_after;   
    END LAST_BUSINESS_DAY;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-06-23
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 1970-01-01
      • 1970-01-01
      • 2015-06-28
      • 1970-01-01
      相关资源
      最近更新 更多