J'ai une condition requise sur laquelle je dois convertir un modèle de récurrence RFC 2445
en dates
en utilisant PLSQL.
Exemple:
RRULE = FREQ=DAILY;INTERVAL=5;COUNT=10
À partir de cette règle, j'ai besoin d'écrire un tableau avec les 10 prochaines occurrences de ce modèle. Quelque chose comme l'image ci-dessous, en considérant la date de début comme 1/1/2019 12:00:00
:
Est-ce qu'Oracle fournit un package PLSQL qui me permet de faire cela? Si ce n'est pas le cas, est-ce que quelqu'un connaît une initiative de projet PLSQL pour cela?
Ps: c'est exactement le même modèle qu'Oracle utilise sur les plans de travail.
3 Réponses :
Vous pouvez y parvenir en utilisant la requête connect by
, mais vous devez trouver le moyen d'obtenir la fréquence et le nombre (utilisez regexp
) et les utiliser dans la requête ci-dessous: < pre> XXX
Cheers !!
Cette solution ne fonctionnera que pour une date spécifique. Je crois que regisxp recherche une solution générique pour n'importe quelle chaîne de calendrier.
Oui, mais que se passe-t-il s'ils veulent utiliser HOURLY à la place, ou BYDAY, etc. Ils devraient personnaliser chacun d'eux.
DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING
pourrait être en mesure de le faire.
La syntaxe prise en charge par le package semble similaire à RFC 2445, mais pas identique. Le bloc PL / SQL ci-dessous imprime les dates en fonction d'une chaîne de calendrier. Il y a quelques complications, telles que l'analyse de COUNT = 10
pour déterminer combien de fois répéter le calcul.
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
Sortie :
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; /
"Et je ne peux pas faire revenir la fonction aujourd'hui comme première itération." - utilisez simplement v_next_run_date: = p_start_date-1;
(ou v_next_run_date: = localtimestamp-1;
) pour la date de début initiale. L'utilisation de v_next_run_date: = p_start_date-1/24/60/60;
couvrira également des intervalles plus petits, par exemple heure.
Vous pouvez écrire une fonction PL / SQL pour analyser la chaîne et générer une collection de dates en pipeline:
Configuration d'Oracle :
| 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 |
Requête :
SELECT * FROM TABLE( parseRRule( rrule => 'FREQ=DAILY;INTERVAL=5;COUNT=10', start_date => DATE '2019-01-01' ) )
Sortie :
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; /
db fiddle ici
Ajouter des jours Oracle SQL
Boucle PL / SQL FOR
Veuillez ne pas publier d'images de code ou de données. Merci.