Oracle SQL за последний рабочий день текущего месяца, включая федеральные праздники в оракуле - PullRequest
0 голосов
/ 23 октября 2018

Я пытаюсь найти Oracle SQL для поиска последнего рабочего дня текущего месяца, а также последнего рабочего дня предыдущего месяца.В обоих случаях следует учитывать календарь федеральных праздников.

Например:

  • Последний рабочий день текущего месяца
  • Если я работаю по состоянию на 15 ноября 2019 года

Технически я должен получить свою продукцию 29 ноября, поскольку 28-й - День благодарения.

Ответы [ 2 ]

0 голосов
/ 24 октября 2018

Работа с праздничным столом в целом работает, однако требует определенных затрат на обслуживание, поскольку некоторые праздничные дни движутся.Например, День благодарения - 4-й четверг ноября, т.е. он варьируется с 22 ноября по 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;
/

Посмотрите на Синтаксис календаря , чтобы увидеть, как нужно указать 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;
0 голосов
/ 23 октября 2018

Сохраните эти федеральные праздники в таблице праздничных дней в виде DATE и попробуйте что-то вроде этого: найдите самый старый (MAX) день за последние семь дней месяца, который не является ни субботой, ни воскресением, нипраздник упоминается в таблице праздников.

Предположения здесь таковы: 1) не все семь дней в конце месяца могут быть праздниками или выходными, и 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 );

Гораздо лучше было бы иметь таблицу Calendar со всеми датамигод и предопределенный столбец с именем isbusinessday.Тогда запрос будет намного проще.

SELECT MAX(dt)
  FROM calendar
  WHERE isbusinessday = 'Y' 
AND TO_CHAR(dt,'YYYYMM') = TO_CHAR(SYSDATE,'YYYYMM');
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...