在11g release 2(11.2)之前定时任务在包DBMS_JOB下,在其之后由DBMS_SCHEDULER替代,而DBMS_JOB仍然可以使用,但墙裂建议使用DBMS_SCHEDULER。

Oracle strongly recommends that you switch from DBMS_JOB to Oracle Scheduler.


DBMS_JOB


查看任务:

  1. select * from DBA_JOBS;

创建任务:

  1. declare jobno number;
  2. BEGIN
  3. DBMS_JOB.SUBMIT(job => jobno,
  4. what => 'STOCK_SNAPSHOT;',--行为,此处写的是存储过程,你也可以直接写SQL语句,不过要注意的是最后要记得加';'
  5. next_date => sysdate,--下次执行时间
  6. interval => 'TRUNC(last_day(sysdate)+1)' --任务执行间隔时间,此处为每个月1号的凌晨
  7. );
  8. end;

修改任务:

  1. BEGIN
  2. DBMS_JOB.WHAT(2,'select 1 from dual;');--DBMS_JOB包下job的属性修改就是使用与属性同名的函数,第一个参数为jobid,第二个为内容
  3. end;

删除任务:

  1. begin
  2. DBMS_JOB.REMOVE(2);--2就是jobid
  3. end;

参考链接:Oracle docs

DBMS_SCHEDULER


查看任务:

  1. select * from DBA_SCHEDULER_JOBS;

创建任务:

  1. begin
  2. DBMS_SCHEDULER.CREATE_JOB(JOB_NAME => 'stock_snapshot_schedule',--job nameDBMS_JOB包没有的
  3. JOB_TYPE => 'STORED_PROCEDURE',--job typeDBMS_JOB包没有,用来指定行为类型,这里是存储过程,还有PLSQL_BLOCK
  4. job_action => 'WLPLAT.STOCK_SNAPSHOT',--相当于DBMS_JOBWHAT
  5. START_DATE => SYSDATE,--任务开始时间
  6. --REPEAT_INTERVAL => 'TRUNC(last_day(sysdate)+1)',--任务执行间隔
  7. REPEAT_INTERVAL => 'freq=monthly;bymonthday=-1;byhour=23'
  8. COMMENTS => '每月1号凌晨将库存三张表转移至快照表');--comments
  9. end;

两种REPEAT_INTERVAL的创建方式:
使用Calendar语法
使用PL/SQL

测试时间间隔:

  1. DECLARE
  2. startdate date;
  3. dateafter date;
  4. nextdate date;
  5. begin
  6. startdate := SYSDATE;
  7. dateafter := startdate;
  8. for i in 1..24 loop
  9. dbms_scheduler.evaluate_calendar_string(
  10. 'FREQ=MONTHLY;BYMONTHDAY=-1;BYHOUR=23'
  11. , startdate
  12. , dateafter
  13. , nextdate
  14. );
  15. dbms_output.put_line(to_char(nextdate,'YYYY-MM-DD HH24:MI'));
  16. dateafter := nextdate;
  17. end loop;
  18. end;

修改任务:

  1. BEGIN
  2. DBMS_SCHEDULER.SET_ATTRIBUTE (
  3. name => 'update_sales',--名字肯定是不可以改但必须提供的,除了名字外所有属性都可以更改
  4. attribute => 'repeat_interval',--属性
  5. value => 'freq=weekly; byday=wed');--值
  6. END;
  7. /

删除任务:

  1. --删除多个JOB用逗号隔开,并且可以提供jobclass并删除此class下的所有job
  2. --jobclass即为job的类别,在创建时可以指定
  3. BEGIN
  4. DBMS_SCHEDULER.DROP_JOB ('job1, job3, sys.jobclass1, sys.jobclass2');
  5. END;
  6. /

执行任务:

  1. --RUN_JOB可以不按计划的执行一次任务
  2. begin
  3. DBMS_SCHEDULER.RUN_JOB(JOB_NAME => 'STOCK_SNAPSHOT_SCHEDULE',USE_CURRENT_SESSION => FALSE);
  4. end;

启动任务:

  1. --与DBMS_JOB不同的是DBMS_SCHEDULER创建的任务默认是关闭的,我们需要手动开启,否则并不会按计划执行
  2. --禁用即为DISABLE
  3. BEGIN
  4. DBMS_SCHEDULER.ENABLE('STOCK_SNAPSHOT_SCHEDULE');
  5. end;

复制任务:

  1. --copy原任务的所有属性
  2. begin
  3. DBMS_SCHEDULER.COPY_JOB('job3','job2');
  4. end;

查看任务日志:

  1. SELECT * FROM (
  2. SELECT t.*
  3. FROM SYS.DBA_SCHEDULER_JOB_RUN_DETAILS t
  4. ORDER BY LOG_DATE DESC
  5. ) WHERE ROWNUM <= 501
  6. SELECT * FROM (
  7. SELECT t.*
  8. FROM SYS.DBA_SCHEDULER_JOB_LOG t
  9. ORDER BY LOG_DATE DESC
  10. ) WHERE ROWNUM <= 501

定时任务使用时碰到的问题:
在项目中设置过任务之后就不管了,以为万无一失,结果到时间却没自动执行,排查了很久终于发现原来是时区的问题

  1. select current_timestamp from dual;--本地时区
  2. --输出2019-12-31 08:15:01.453053 +00:00Greenwitch Mean Time
  1. select systimestamp from dual;--数据库系统时区
  2. --输出2019-12-31 16:13:59.775550 +08:00,东八区

时区不一致,在这个例子中系统时间是东八区而创建任务的是格林时间所以任务会延迟(也就是延迟8小时),于是我们在创建任务的时候要改一点(你也可以将自己本地session的时区更改为正确的)

  1. begin
  2. DBMS_SCHEDULER.CREATE_JOB(JOB_NAME => 'stock_snapshot_schedule',
  3. JOB_TYPE => 'STORED_PROCEDURE',
  4. job_action => 'WLPLAT.STOCK_SNAPSHOT',
  5. START_DATE => systimestamp, --SYSDATE改为systimestamp
  6. --如果是自定义时间,可以用to_timestamp_tz函数将字符串转换成带时区的时间
  7. --select to_timestamp_tz('2010-01-03 11:09:00 +8:00','YYYY-MM-DD HH24:MI:SS TZH:TZM') from dual;
  8. REPEAT_INTERVAL => 'freq=monthly;bymonthday=-1;byhour=23'
  9. COMMENTS => '每月1号凌晨将库存三张表转移至快照表');--comments
  10. end;

参考链接:Oracle docs
参考链接:why are my jobs not running