Cách sử dụng DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING

Từ Oracle Database 11g, Oracle đã giới thiệu một hàm trong package DBMS_SCHEDULER để tính toán các lần chạy tiếp theo từ một chuỗi lịch theo cấu trúc dễ hiểu, đó chính là:

DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING

Chúng ta thử bắt đầu với ví dụ đơn giản sau:

declare
    v_cal_string varchar2(4000);
    v_next_date timestamp;
begin
    dbms_output.put_line('Now is : '||to_char(sysdate, 'DY DD-MON-YYYY HH24:MI'));
    -- Calendar string for every day at 06:15
    v_cal_string := 'FREQ=DAILY; BYHOUR=6; BYMINUTE=15';
     
    dbms_scheduler.evaluate_calendar_string( 
        calendar_string => v_cal_string,
        start_date => null,
        return_date_after => null,
        next_run_date => v_next_date);
         
    dbms_output.put_line('Next Run Date is : '||to_char(v_next_date, 'DY DD-MON-YYYY HH24:MI'));    
     
end;
/

Chạy nó và chúng ta có thể thấy được kết quả biến next_run_date chính là sáng ngày hôm sau:

Lưu ý rằng tất cả các tham số của EVALUATE_CALENDAR_STRING là bắt buộc.

Một ví dụ thú vị hơn, giả sử chúng ta cần tính ngày trả lương là ngày thứ 5 thứ 3 hàng tháng:

declare
    v_cal_string varchar2(4000);
    v_next_date timestamp;
begin
    dbms_output.put_line('Now is : '||to_char(sysdate, 'DY DD-MON-YYYY HH24:MI'));
    v_cal_string := 'FREQ=MONTHLY; BYDAY=3 THU; BYHOUR=0; BYMINUTE=0';
     
    dbms_scheduler.evaluate_calendar_string( 
        calendar_string => v_cal_string,
        start_date => null,
        return_date_after => null,
        next_run_date => v_next_date);
         
    dbms_output.put_line('Next Payday is : '||to_char(v_next_date, 'DY DD-MON-YYYY HH24:MI'));    
     
end;
/

Nếu bạn muốn trả về kết quả ngày sau một ngày cụ thể thì hãy sử dụng tham số: RETURN_DATE_AFTER

declare
    v_weekday_string varchar2(4000) := 'FREQ=DAILY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=6; BYMINUTE=15';
    v_next_run timestamp;
    v_start_from timestamp;
begin
    dbms_output.put_line('Weekday...');
    -- I want to know that the calendar string will hit Monday - Friday but not Saturday or Sunday
    v_start_from := to_timestamp(next_day(sysdate, 'TUESDAY'));
 
    for i in 1..5 loop
        
        dbms_scheduler.evaluate_calendar_string(
            calendar_string => v_weekday_string,
            start_date => null,
            return_date_after => v_start_from,
            next_run_date => v_next_run);
        dbms_output.put_line(to_char( v_next_run, 'Day DDth Month YYYY HH24:MI'));
        -- set RETURN_DATE_AFTER to be the date last found by the string
        v_start_from := v_next_run;
    end loop;    
     
end;
/

Ngoài ra chúng ta còn có thể sử dụng tham số start_date

declare
    v_weekend_string varchar2(4000) := 'FREQ=DAILY; BYDAY=SAT,SUN; BYHOUR=9; BYMINUTE=30'; 
    -- I like a lie-in at weekends 
     
    v_start_date timestamp;
    v_next_run timestamp;
 
begin
    dbms_output.put_line('Weekday...');
    v_start_date := to_timestamp(next_day(systimestamp, 'MONDAY'));
    for i in 1..2 loop
         
        dbms_scheduler.evaluate_calendar_string(
            calendar_string => v_weekend_string,
            start_date => v_start_date,
            return_date_after => null,
            next_run_date => v_next_run);
        dbms_output.put_line(to_char( v_next_run, 'Day DDth Month YYYY HH24:MI'));
        -- Unlike the RETURN_DATE_AFTER parameter, we need to increment the start date by a day
        -- in each loop iteration
        v_start_date := v_next_run + 1;
    end loop;    
end;
/

Nếu có cách sử dụng thú vị hơn, mời bạn chia sẻ với mọi người trong phần bình luận nhé.