Oracle PLSQL 重复模式 RFC 2445
Oracle PLSQL Recurrence Pattern RFC 2445
我需要使用 PLSQL 将 RFC 2445
循环模式转换为 Dates
。
示例:
RRULE = FREQ=DAILY;INTERVAL=5;COUNT=10
根据该规则,我需要用该模式接下来出现的 10 次写一个 table。类似于下图,考虑开始日期为 1/1/2019 12:00:00 AM
:
Oracle 是否提供任何允许我执行此操作的 PLSQL 程序包?如果没有,有人知道这方面的任何 PLSQL 项目倡议吗?
Ps:这与 Oracle 在作业计划中使用的模式完全相同。
您可以使用 connect by
查询来实现此目的,但您需要找出获取频率和计数的方法(使用 regexp
)并在以下查询中使用它们:
Select date '2019-01-01' + (level-1) * 5 as dates
From dual
Connect by level <= 10;
干杯!!
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
您可以编写一个 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<>fiddle here
我需要使用 PLSQL 将 RFC 2445
循环模式转换为 Dates
。
示例:
RRULE = FREQ=DAILY;INTERVAL=5;COUNT=10
根据该规则,我需要用该模式接下来出现的 10 次写一个 table。类似于下图,考虑开始日期为 1/1/2019 12:00:00 AM
:
Oracle 是否提供任何允许我执行此操作的 PLSQL 程序包?如果没有,有人知道这方面的任何 PLSQL 项目倡议吗?
Ps:这与 Oracle 在作业计划中使用的模式完全相同。
您可以使用 connect by
查询来实现此目的,但您需要找出获取频率和计数的方法(使用 regexp
)并在以下查询中使用它们:
Select date '2019-01-01' + (level-1) * 5 as dates
From dual
Connect by level <= 10;
干杯!!
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
您可以编写一个 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<>fiddle here