Шаблон повторения Oracle PLSQL RFC 2445 - PullRequest
1 голос
/ 07 октября 2019

У меня есть реквизит, по которому мне нужно преобразовать шаблон повторения RFC 2445 в Dates с использованием PLSQL.

Пример:

RRULE = FREQ=DAILY;INTERVAL=5;COUNT=10

Из этого правила мне нужнонаписать таблицу со следующими 10 вхождениями этого шаблона. Что-то вроде изображения ниже, учитывая дату начала как 1/1/2019 12:00:00 AM:

enter image description here

Предоставляет ли Oracle какой-либо пакет PLSQL, который позволяет мне это делать? Если нет, знает ли кто-нибудь какую-либо инициативу проекта PLSQL для этого?

Ps: это тот же самый шаблон, который Oracle использует в расписаниях заданий.

Ответы [ 3 ]

2 голосов
/ 07 октября 2019

DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING может быть в состоянии сделать это.

Синтаксис, поддерживаемый пакетом, похож на RFC 2445, но не идентичен. Приведенный ниже блок PL / SQL выводит даты на основе календарной строки. Есть некоторые сложности, такие как разбор COUNT=10, чтобы определить, сколько раз повторить вычисление.

declare
    --Test different calendar strings and start dates.
    --p_calendar_string varchar2(4000) := 'FREQ=DAILY;INTERVAL=5;';
    p_calendar_string varchar2(4000) := 'FREQ=DAILY;INTERVAL=5;COUNT=10';
    p_start_date date := timestamp '2019-01-01 00:00:00';

    v_next_run_date date;
    v_count number;
    --Find the COUNT and remove it rom the calendar string, if it exists.
    procedure get_and_remove_count(p_calendar_string in out varchar2, p_count out number) is
    begin
        if lower(p_calendar_string) like '%count%' then
            p_count := to_number(regexp_substr(p_calendar_string, 'COUNT=([0-9]+)', 1, 1, null, 1));
            p_calendar_string := regexp_replace(p_calendar_string, 'COUNT=[0-9]+;?');
        else
            p_count := 1;
        end if;
    end;
begin
    get_and_remove_count(p_calendar_string, v_count);

    --TEST
    --dbms_output.put_line('String: '||p_calendar_string||', count: '||v_count);

    --Start with the original date.
    v_next_run_date := p_start_date-1/24/60/60;

    --Loop through the COUNT and display all dates.
    for i in 1 .. v_count loop

        dbms_scheduler.evaluate_calendar_string
        (
            calendar_string   => p_calendar_string,
            start_date        => p_start_date,
            return_date_after => v_next_run_date,
            next_run_date     => v_next_run_date
        );

        dbms_output.put_line(to_char(v_next_run_date, 'mm/dd/yyyy hh:mi:ss am'));
    end loop;
end;
/

Выход:

01/01/2019 12:00:00 am
01/06/2019 12:00:00 am
01/11/2019 12:00:00 am
01/16/2019 12:00:00 am
01/21/2019 12:00:00 am
01/26/2019 12:00:00 am
01/31/2019 12:00:00 am
02/05/2019 12:00:00 am
02/10/2019 12:00:00 am
02/15/2019 12:00:00 am
2 голосов
/ 07 октября 2019

Вы можете написать функцию PL / SQL для анализа строки и вывода конвейерной коллекции дат:

Установка Oracle :

CREATE FUNCTION parseRRule(
  rrule      IN VARCHAR2,
  start_date IN DATE
) RETURN SYS.ODCIDATELIST PIPELINED
IS
  freq    VARCHAR2(10) := UPPER( REGEXP_SUBSTR( rrule, '(^|;)FREQ=(MONTHLY|WEEKLY|DAILY|HOURLY)(;|$)', 1, 1, 'i', 2 ) );
  inter   NUMBER(4,0)  := TO_NUMBER( REGEXP_SUBSTR( rrule, '(^|;)INTERVAL=(\d+)(;|$)', 1, 1, 'i', 2 ) );
  cnt     NUMBER(4,0)  := TO_NUMBER( REGEXP_SUBSTR( rrule, '(^|;)COUNT=(\d+)(;|$)', 1, 1, 'i', 2 ) );
  dt      DATE         := start_date;
  step_ds INTERVAL DAY TO SECOND;
  step_m  NUMBER(3,0);
BEGIN
  IF freq IS NULL OR inter IS NULL OR cnt IS NULL OR dt IS NULL THEN
    RETURN;
  END IF;
  IF freq = 'MONTHLY' THEN
    step_ds := INTERVAL '0' DAY;
    step_m  := inter;

  ELSIF freq = 'WEEKLY' THEN
    step_ds := inter * INTERVAL '7' DAY;
    step_m  := 0;
  ELSIF freq = 'DAILY' THEN
    step_ds := inter * INTERVAL '1' DAY;
    step_m  := 0;
  ELSIF freq = 'HOURLY' THEN
    step_ds := inter * INTERVAL '1' HOUR;
    step_m  := 0;
  ELSE
    NULL;
    -- raise exception
  END IF;
  PIPE ROW ( dt );
  FOR i IN 1 .. cnt - 1 LOOP
    dt := ADD_MONTHS( dt + step_ds, step_m );
    PIPE ROW ( dt );
  END LOOP;
END;
/

Запрос :

SELECT *
FROM   TABLE(
  parseRRule(
    rrule      => 'FREQ=DAILY;INTERVAL=5;COUNT=10',
    start_date => DATE '2019-01-01'
  )
)

Вывод :

| COLUMN_VALUE |
| :----------- |
| 2019-01-01   |
| 2019-01-06   |
| 2019-01-11   |
| 2019-01-16   |
| 2019-01-21   |
| 2019-01-26   |
| 2019-01-31   |
| 2019-02-05   |
| 2019-02-10   |
| 2019-02-15   |

db <> скрипка здесь

0 голосов
/ 07 октября 2019

Вы можете достичь этого с помощью запроса connect by, но вам нужно выяснить способ получения частоты и количества (используйте regexp) и использовать их в следующем запросе:

Select date '2019-01-01' + (level-1) * 5 as dates
From dual
Connect by level <= 10;

Cheers !!

...