Oracle 10g Scheduler 特性

Oracle 10g Scheduler 特性 
在10g 环境中,ORACLE 建议使用Scheduler 替换普通的job,来管理任务的执行。其实,将Scheduler 描述成管理job 的工具已经太过片面了,10G版本中新增的Scheduler 绝不仅仅是创建任务这么简单.


一. 使用Jobs

所谓JOBS,其实就是Scheduler 管理的一个(或多个)任务的执行调度。


1.1 创建Jobs

通过DBMS_SCHEDULER 包来创建Jobs,是使用其CREATE_JOB 过程。在创建Job 时,用户可以指定要执行的任务,调度信息(啥时候执行,执行周期,终止日期等)以及其它一些任务相关的属性。CREATE_JOB 过程调用还是比较简单的,例如:

create table TEST (id number);


CREATE OR REPLACE PROCEDURE IT

AS

BEGIN

insert into TEST VALUES(1);

END;


SQL> BEGIN

DBMS_SCHEDULER.CREATE_JOB (

job_name => 'JobTest',

job_type => 'STORED_PROCEDURE',

job_action => 'SYSTEM.IT',

start_date => sysdate,

repeat_interval => 'FREQ=MINUTELY;INTERVAL=10');

END;

/

PL/SQL 过程已成功完成。


事实上,有权限的话,用户也可以创建其它SCHEMA 下的JOB,只需要在指定JOB_NAME 时,按照schema.job_name 的格式即可。注意哟,这种情况下创建的JOB,其CREATED 与OWNER 有可能并不相同的哟。

当使用CREATE_JOB 过程创建JOB 时,可指定的参数值很多,只不过多数情况下用户仅指定部分参数即可满足需求。


其中,上例中指定的参数,分别代表的含义如下:

JOB_NAME:指定任务的名称,必选值,注意要确保指定的名称唯一。

JOB_TYPE:任务执行的操作类型,必选值,有下列几个可选值:

   PLSQL_BLOCK:表示任务执行的是一个PL/SQL 匿名块。

   STORED_PROCEDURE:表示任务执行的是ORACLE 过程(含PL/SQL PROCEDURE 和JAVA

PROCEDURE),本例中正是指定这一参数值。

   EXECUTABLE:表示任务执行的是一个外部程序,比如说操作系统命令。

   CHAIN:表示任务执行的是一个CHAIN。

JOB_ACTION:任务执行的操作,必选值,应与JOB_TYPE 类型中指定的参数相匹配。

比如说对于PL/SQL 匿名块,此处就可以放置PL/SQL 块的具体代表,类似DECLARE .. BEGIN ..END这类;如果是ORACLE 过程,那么此处应该指定具体的过程名,注意由于任务执行,即使过程中有OUT之类参数,实际执行时也不会有输出的。

START_DATE:指定任务初次执行的时间,本参数可为空,当为空时,表示任务立刻执行,效果等同于指定该参数值为SYSDATE。

REPEAT_INTERVAL:指定任务执行的频率,比如多长时间会被触发再次执行。本参数也可以为空,如果为空的话,就表示当前设定的任务只执行一次。REPEAT_INTERVAL 参数需要好好说说,因为这一参数与标准JOB 中的INTERVAL 参数有很大区别,相比之下,REPEAT_INTERVAL 参数的语法结构要复杂的多。其中最重要的是FREQ 和INTERVAL 两个关键字。

   FREQ 关键字用来指定间隔的时间周期,可选参数有:YEARLY, MONTHLY, WEEKLY, DAILY,HOURLY, MINUTELY, and SECONDLY,分别表示年、月、周、日、时、分、秒等单位。

   INTERVAL 关键字用来指定间隔的频繁,可指定的值的范围从1-99。

例如:REPEAT_INTERVAL=>'FREQ=DAILY;INTERVAL=1';表示每天执行一次,如果将INTERVAL 改为7 就表示每7 天执行一次,效果等同于FREQ=WEEKLY;INTERVAL=1。

一般来说,使用DBMS_SCHEDULER.CREATE_JOB 创建一个JOB,至少需要指定上述参数中的前3 项。除此之外,还可以在CREATE_JOB 时,指定下列参数:

   NUMBER_OF_ARGUMENTS:指定该JOB 执行时需要附带的参数的数量,默认值为0,注意当JOB_TYPE 列值为PLSQL_BLOCK 或CHAIN 时,本参数必须设置为0,因为上述两种情况下不支持附带参数。

   END_DATE:指定任务的过期时间,默认值为NULL。任务过期后,任务的STATE 将自动被修改为COMPLETED,ENABLED 被置为FALSE。如果该参数设置为空的话,表示该任务永不过期,将一直按照

REPEAT_INTERVAL 参数设置的周期重复执行,直到达到设置的MAX_RUNS 或MAX_FAILURES 值。

   JOB_CLASS:指定任务关联的CLASS,默认值为DEFAULT_JOB_CLASS。

   ENABLED:指定任务是否启用,默认值为FALSE。FALSE 状态表示该任务并不会被执行,除非被用户手动调用,或者用户将该任务的状态修改为TRUE。

   AUTO_DROP:当该标志被置为TRUE 时,ORACLE 会在满足条件时自动删除创建的任务

   任务已过期;

   任务最大运行次数已达MAX_RUNS 的设置值;

   任务未指定REPEAT_INTERVAL 参数,仅运行一次;

该参数的默认值即为TRUE。用户在执行CREATE_JOB 过程时可以手动将该标志指定为FALSE,当参数值设置为FALSE 时,即使满足上述提到的条件任务也不会被自动删除,这种情况下,唯一能够导致任务被删除的情况,就是用户主动调用DROP_JOB 过程。

   COMMENTS:设置任务的注释信息,默认值为NULL。


上面的例子创建了一个新的JOB,不过这个JOB 与普通JOB 不同哟,此时查询USER_JOBS 视图是查不到刚刚创建的JOB 的信息,因为这个JOB 是SCHEDULER 管理的JOB。要查询SCHEDULER 管理的JOS,应该通过USER_SCHEDULER_JOBS(当然ALL_SCHEDULER_JOBS 和DBA_SCHEDULER_JOBS 也可以), 例如:


SQL> select job_name,job_type,job_action,to_char(start_date,'yyyy-mm-dd

hh24:mi:ss') TM,repeat_interval,enabled,state from user_scheduler_jobs;

JOB_NAME   JOB_TYPE         JOB_ACTION TM         REPEAT_INTERVAL           ENABL STATE

---------- ---------------- ---------- ---------- ------------------------- ------ ------

JOBTEST    STORED_PROCEDURE SYSTEM.IT  2009-09-25 FREQ=MINUTELY;INTERVAL=10  FALSE DISABLED


不过,细心的盆友可能会发现,JOB 虽然成功创建了,但却并未执行.原因ENABLED 参数当不显式指定时,该参数的默认值为false,JOB自然不会运行了。


1.2 管理Jobs

1.2.1 启用Jobs

前面创建JOB 时,由于未显式的指定ENABLED 参数,因此即使指定了START_DATE,不过默认情况下JOB

不会自动执行。对于这种情况,DBMS_SCHEDULER 包中提供了一个过程ENABLE,可以用来修改JOB 的启

用状态,调用方式非常简单,例如:

SQL> exec dbms_scheduler.enable('JOBTEST');

PL/SQL procedure successfully completed.


1.2.2 禁用Jobs

DBMS_SCHEDULER.ENABLE 仅用来将JOB(其实不仅仅对JOB 有效,对于CHAIN、PROGRAM 等也有效)的启用状态置为TRUE。如果想将其启用状态置为FALSE?简单,还有一个与该功能对应的过程:

DBMS_SCHEDULER.DISABLE,例如:

JSSWEB> exec dbms_scheduler.disable('JOBTEST');

PL/SQL procedure successfully completed.

这两个过程仅用来重置对象的状态,因此均可以无限次执行,即使执行时对象已经被置为要指定的状态。


1.2.3 修改Jobs

由于JOB 的属性众多,难免时不时的可能会遇到需要修改的情况,比如说前面创建JOB 时不小心,指定要

执行的过程名输入错误(完全有可能,CREATE_JOB 在创建时不会自动检查指定的过程是否有效,从这方面考虑,

SCHEDULER 不如普通JOB 严谨哪),这种情况下就必然涉及到对JOB 的修改(或者说重定义),没问题,

DBMS_SCHEDULER 包中专门提供了一个过程SET_ATTRIBUTE,可以用来修改任务的属性值。

例如,修改刚刚创建的JOB:INSERT_TEST_TBL 执行的过程,执行语句如下:

JSSWEB> exec dbms_scheduler.set_attribute('JobTest','JOB_ACTION','SYSTEM.IT');

PL/SQL procedure successfully completed

当然啦,我们这里执行的这条语句,执行跟没执行没有区别,此处仅做示例,大家表深究。


SET_ATTRIBUTE 过程虽然仅有三个参数,不过能够修改的属性值可是不少,以下列举几个较常用到的:

   LOGGING_LEVEL:指定对jobs 执行情况记录的日志信息级别。

SCHEDULER 管理的JOB 对任务的执行情况专门进行了记录,同时用户还可以选择日志中记录信息的级别,有下列三种选择:

   DBMS_SCHEDULER.LOGGING_OFF:关闭日志记录功能;

   DBMS_SCHEDULER.LOGGING_RUNS:对任务的运行信息进行记录;

   DBMS_SCHEDULER.LOGGING_FULL:记录任务所有相关信息,不仅有任务的运行情况,甚至连任务的创建、修改等也均将记入日志。

提示: 查看SCHEDULER 管理的JOB , 可以通过USER_SCHEDULER_JOB_LOG 和USER_SCHEDULER_JOB_RUN_DETAILS 两个视图中查询

   RESTARTABLE:指定jobs 运行出错后,是否能够适时重启创建任务时如未明确指定,本参数默认情况下设置为FALSE,如果设置为TRUE,就表示当任务运行时出错,下次运行时间点到达时仍会启动,并且如果运行仍然出错,会继续重新运行,不过如果连接出错达到6 次,该job 就会停止。

   MAX_FAILURES:指定jobs 最大连续出错次数该参数值可指定的范围从1-1000000,默认情况下该参数设置为NULL,表示无限制。达到指定出错次数后,该job 会被自动disable。

   MAX_RUNS:指定jobs 最大运行次数

该参数值可指定的范围从1-1000000,默认情况下该参数设置为NULL,表示无限制(只是运行次数无限制,实际job 会否继续运行,仍受制于end_date 以及max_failures 等参数的设置)。达到指定运行次数后,该job 也将被自动disable,并且状态会被置为COMPLETED。

   JOB_TYPE:指定job 执行的任务的类型

有四个可选值:'PLSQL_BLOCK', 'STORED_PROCEDURE', 'EXECUTABLE', and 'CHAIN'。

   JOB_ACTION:指定job 执行的任务.这一参数所指定的值依赖于JOB_TYPE 参数中的值, 比如说JOB_TYPE 设置为

'STORED_PROCEDURE',那么本参数值中指定的一定是ORACLE 中的过程名。

   START_DATE:指定job 初次启动的时间

   END_DATE:指定job 停止运行的时间

本参数又与AUTO_DROP 相关联,如果AUTO_DROP 设置为TRUE 的话,那么一旦job 到达停止运行的时间,该job 就会被自动删除,否则的话job 任何存在,不过状态被修改为COMPLETED。

除此之外, 其它还包括MAX_RUN_DURATION , JOB_WEIGHT , INSTANCE_STICKINESS ,STOP_ON_WINDOW_CLOSE , JOB_PRIORITY , SCHEDULE_LIMIT , PROGRAM_NAME ,NUMBER_OF_ARGUMENTS , SCHEDULE_NAME , REPEAT_INTERVAL , JOB_CLASS , COMMENTS ,AUTO_DROP,EVENT_SPEC,RAISE_EVENTS 等等,这些参数所代表的意义此处不一一详述,感兴趣的朋友


仅从这些可设置属性就可以看出,Scheduler 管理的job 确实非常灵活,上述提到了这些参数,均可以使用

DBMS_SCHEDULER.SET_ATTRIBUTE 过程进行设置。


另外需要注意一点,除了用户手动创建的jobs 之外,数据库在运行过程中也有可能自动创建jobs。对于这

类jobs 除非必要,否则不建议进行修改。至于如何区分jobs 是用户创建,还是数据库自动创建,可以通过

*_SCHEDULER_JOBS 视图的SYSTEM 列来确定,如果该列显示为TRUE,则表示由系统创建


1.2.4 执行Jobs

虽然说jobs 大多都应该是自动执行,不过经过前面的示例,大家想必也认识到了,并不是说创建了jobs 它

就会自动执行,是否能够真正自动执行并不是由你的主观意愿就能直接决定,而是由jobs 自身的多个相关属性

决定。

关于jobs 自动执行的话题相信看完前面的内容后,应该都知道如何设置,下面主要演示,如何手动调用jobs

并执行,这其中,当然少不了DBMS_SCHEDULER 包。例如,手动执行前面刚刚创建的job:JOBTEST:

JSSWEB> exec dbms_scheduler.run_job('JOBTEST');

PL/SQL procedure successfully completed

Jobs 每执行一次,无论成功或失败,均会在*_SCHEDULER_JOB_LOG 中生成一条对应的记录(前提是LOGGING_LEVEL 属性值未设置为DBMS_SCHEDULER.LOGGING_OFF) , 同时, 用户也可以通过*_SCHEDULER_JOB_RUN_DETAILS 视图查询job 执行的详细信息。


1.2.5 停止Jobs

停止job 可以使用DMBS_SCHEDULER.STOP_JOB 过程,例如:

JSSWEB> exec dbms_scheduler.stop_job('JOBTEST');

PL/SQL procedure successfully completed


注意,STOP_JOB 过程不仅仅是更新job 的状态,而是停止当前正在执行的任务,如果你处理的任务当前未在运行的话,那么执行STOP_JOB 过程,会触发ORA-27366 错误。

停止Jobs 也会触发一条任务的日志信息,对于执行停止操作的job,其*_SCHEDULER_JOB_LOG 视图的OPERATION 会记录为'STOPPED',ADDITIONAL_INFO 列中记录的信息类似'REASON="Stop job called by user:username"'。


1.2.6 删除Jobs

删除创建的job 就比较简单了,直接执行DBMS_SCHEDULER.DROP_JOB 过程即可,例如:

JSSWEB> exec dbms_scheduler.drop_job('JOBTEST');

PL/SQL procedure successfully completed

删除jobs 并不是修改该job 中某个字段的标记值,而是直接删除其在数据字典中的字义,因此被删除的job

如果未来发现仍然需要,只能重建,而无法通过其它方式快速恢复。不过,删除jobs 的操作,并不会级联删除

这些job 曾经执行过的日志信息。


二、使用Programs

进入10g 版本之后,可以在ORACLE 中执行操作系统命令,或是ORACLE 数据库外的应用,因为有了DBMS_SCHEDULER,因为有了PROGRAM。


2.1 创建Programs

Scheduler 中的Program 对象并不是常规意义上的"程序"或"应用",而就是一个"对象",由DBA 定义的,具

有执行某项功能的特殊对象。Program 中实际执行的操作可以分为下列三种类型:

   PL/SQL BLOCK:标准的pl/sql 代码块;

   STORED PROCEDURE:编译好的PL/SQL 存储过程,或者Java 存储过程,以及外部的子程序;

   EXECUTEABLE:ORACLE 数据库之外的应用,比如操作系统命令等等。

创建Programs 使用DBMS_SCHEDULER.CREATE_PROGRAM 过程,该过程支持的参数如下:

SQL> desc dbms_scheduler.create_program;

Parameter Type Mode Default

------------------- -------------- ---- --------

PROGRAM_NAME VARCHAR2 IN

PROGRAM_TYPE VARCHAR2 IN

PROGRAM_ACTION VARCHAR2 IN

NUMBER_OF_ARGUMENTS BINARY_INTEGER IN Y

ENABLED BOOLEAN IN Y

COMMENTS VARCHAR2 IN Y

如上所示,前三项为必选参数,各参数实际代表的意义如下:

   PROGRAM_NAME:指定一个program 名称;

   PROGRAM_TYPE:Program 的类型,如前文中所述,Program 支持三种类型;

   PROGRAM_ACTION:实际执行的操作,应与前面PROGRAM_TYPE 参数关联使用。比如说前面指定

了PROGRAM_TYPE 为"PLSQL_BLOCK",那么此处要执行的action 就应当是一段标准的pl/sql 代码。如果前

面指定PROGRAM_TYPE 为"STORED_PROCEDURE",那么此处要执行的action 就应当是ORACLE 中定义好

的存储过程(含Java 存储过程),如果前面指定PROGRAM_TYPE 为"EXECUTABLE",那么此处就应该指定外

部命令的命令行信息(含路径信息);

   NUMBER_OF_ARGUMENTS:指定支持的参数个数,默认值为0 即没有参数。每个program 最多能够

支持255 个参数,注意如果PROGRAM_TYPE 设置为PLSQL_BLOCK,那么本参数自动忽略;

   ENABLED:指定是否将创建的program 置为有效状态,默认情况下为false。

   COMMENTS:这个不用再说了吧,注释信息。

下面实际操作一下看看,PL/SQL 或PROCEDURE 没有挑战(ORACLE 中直接即可调用),咱们创建一下

program,直接调用操作系统中的ls 命令,操作如下:

SQL> BEGIN

DBMS_SCHEDULER.CREATE_PROGRAM (

program_name => 'IPCONFIG',

program_action => 'C:\WINDOWS\system32\ipconfig.exe',

program_type => 'EXECUTABLE',

enabled => TRUE);

END;

/

PL/SQL procedure successfully completed.


2.2 管理Programs

CREATE_PROGRAM过程的参数时提到,每个program最多支持255 个参数,要为program 添加参数,可以通过DEFINE_PROGRAM_ARGUMENT 过程。不过在为其添加参数前,要注意program 的NUMBER_OF_ARGUMENTS 指定的数量,如果该值为0,那么为其添加参数时就会报错。

查询创建的program 的信息,可以通过USER_SCHEDULER_PROGRAMS 视图,例如:

SQL> select program_name,program_type,program_action,number_of_arguments,enabled

from user_scheduler_programs;


由于前面创建program 時並未指定NUMBER_OF_ARGUMENTS 的值,因此我们这里需要首先修改该值为

一个非0 值,操作如下:

SQL> exec dbms_scheduler.set_attribute('IPCONFIG','NUMBER_OF_ARGUMENTS',1);

PL/SQL procedure successfully completed.

没错, 操作还是使用DBMS_SCHEDULER.SET_ATTRIBUTE 过程。另外需要注意, program 的NUMBER_OF_ARGUMENTS 参数可是说想改就能改的,正常情况下该处理必须是在program 处于enabled 之前确认完毕,否则会触发ORA-27465 错误,因此要修改program 的参数之前,必须首先确保要修改program 的enabled 状态为false。


那么对于已经处于enabled 状态的program,如何修改其状态属性呢?其实很简单,前面操作jobs 时使用的

DBMS_SCHEDULER.DISABLE 过程还记的吗?没错,该过程对于program 同样好使,并且调用方式也完全一

样,例如:

SQL> exec dbms_scheduler.disable('IPCONFIG');

PL/SQL procedure successfully completed.

另外,如果希望将program 置为enabled 状态,执行DBMS_SCHEDULER.ENABLE 过程即可,这里不再例举。

 

接下来,就可以为刚刚创建的IPCONFIG添加路径参数,操作如下:

SQL> BEGIN

DBMS_SCHEDULER.DEFINE_PROGRAM_ARGUMENT (

program_name => 'IPCONFIG',

argument_position => 1,

argument_name => 'dirpath',

argument_type => 'VARCHAR2',

default_value => 'C:\');

END;

/

PL/SQL procedure successfully completed.


exec DBMS_SCHEDULER.ENABLE('IPCONFIG');


查询为program 定义的参数,可以通过USER_SCHEDULER_PROGRAM_ARGS 视图,例如:

SQL> select program_name,argument_name,argument_position,argument_type

default_value from user_scheduler_program_args;


删除program 的argument 操作也很简单,使用DROP_PROGRAM_ARGUMENT 过程即可,例如:

SQL> exec dbms_scheduler.drop_program_argument('IPCONFIG','dirpath');

PL/SQL procedure successfully completed.

该过程第一个参数指定program 名称,第二个参数指定定义的argument 名称,当然此处也可以指定argument

的位置,即前例视图返回结果中的ARGUMENT_POSITION 列值。

要删除program 的话就更简单了,使用DROP_PROGRAM 过程即可,例如:

SQL> exec dbms_scheduler.drop_program('IPCONFIG');

PL/SQL procedure successfully completed.

当然啦,删除program 的同时,也会删除该program 对应的所有arguments。


实际上SCHEDULER 中创建job 时,也可以指定执行外部的程序。SCHEDULER 中的Job 更像是之前版本

继承过来的JOBS,只不过10g 中SCHEDULER 管理的JOBS 功能更加强大。Programs 与Jobs 不同的是,Jobs

是定义好的,定时执行的任务,而Programs 则是定义好的,等待被执行的对象。


三、使用Schedules

10g 中新推出的SCHEDULER 可能确实会让很多初接触的朋友感觉晕头晕脑,相比之前的jobs,

SCHEDULER 中新增的概念太多。比如说jobs,仍然可以理解成之前版本中的jobs,不过功能更加强大,比如说program,指的是运行的程序(把要做什么单提出来了),比如说schedule,我将其翻译为调度,定义执行的

频率或者说周期。


3.1 创建和管理Schedules

Schedule,中文直译的话应该理解成调度,从名字来看,它是一个逻辑实体,就是说当创建了schedule 之后,数据库中就肯定存在这一对象,只不过这一对象是用来描述job 的执行周期。

创建schedule 可以通过DBMS_SCHEDULER.CREATE_SCHEDULE 过程,该过程支持的参数如下:

SQL>desc dbms_scheduler

SQL>desc dbms_scheduler.create_schedule;

Parameter Type Mode Default?

--------------- ------------------------ ---- --------

SCHEDULE_NAME VARCHAR2 IN

START_DATE TIMESTAMP WITH TIME ZONE IN Y

REPEAT_INTERVAL VARCHAR2 IN

END_DATE TIMESTAMP WITH TIME ZONE IN Y

COMMENTS VARCHAR2 IN Y

各参数分别代表含意如下:

   SCHEDULE_NAME:指定schedule 名称,注意名称不能重复。

   START_DATE:指定该调度的开始时间,可为空,当为空时表示该调度暂不起用。

   REPEAT_INTERVAL:指定调度的执行频率或周期。

   END_DATE:指定调度的结束时间,可为空,为空时就表示该调度将一直进行。

   COMMENTS:注释信息。

这其中,比较有技术含量的是REPEAT_INTERVAL 参数,对于这个参数大家应该不会太陌生,因为前面介

绍Jobs,也曾经提到过同名的参数,Schedules 中的REPEAT_INTERVAL 参数和Jobs 中的REPEAT_INTERVAL

参数功能完全相同,甚至参数格式也一模一样。

REPEAT_INTERVAL 参数的语法结构要复杂的多。其中最重要的是FREQ 和INTERVAL 两个关键字。

   FREQ 关键字用来指定间隔的时间周期,可选参数有:YEARLY, MONTHLY, WEEKLY, DAILY,

HOURLY, MINUTELY, and SECONDLY,分别表示年、月、周、日、时、分、秒等单位。

   INTERVAL 关键字用来指定间隔的频繁,可指定的值的范围从1-99。

比如说, 当指定REPEAT_INTERVAL=>'FREQ=DAILY;INTERVAL=1';就表示每天执行一次, 如果将

INTERVAL 改为7 就表示每7 天执行一次,效果等同于FREQ=WEEKLY;INTERVAL=1。


下面,创建一个schedule,指定调度为每周一次的频率,执行脚本如下:

SQL> BEGIN

DBMS_SCHEDULER.CREATE_SCHEDULE (

schedule_name => 'MySchedule',

start_date => SYSDATE,

repeat_interval => 'FREQ=WEEKLY; INTERVAL=1',

comments => 'Every 1 weeks');

END;

/

PL/SQL procedure successfully completed.


查询当前已经创建的schedules,可以通过*_SCHEDULER_SCHEDULES 视图(含DBA_,ALL_,USER_),例如,查看当前用户拥有的schedules,执行语句如下:

SQL> select schedule_name,repeat_interval from user_scheduler_schedules;


如果要修改schedule 属性的话,也是使用DBMS_SCHEDULER.SET_ATTRIBUTE 过程,该过程的调用方式前面已经多次演示过,这里就不再重复举例了,仅说明一点,对于schedule 来说,能够修改的属性包括:

REPEAT_INTERVAL、COMMENTS、END_DATE、START_DATE 以及EVENT_SPEC。


至于删除schedule,再简单不过,执行DBMS_SCHEDULER.DROP_SCHEDULE 过程即可,例如:

SQL> EXEC DBMS_SCHEDULER.DROP_SCHEDULE('MY_FIRST_SCHEDULE');

PL/SQL procedure successfully completed.


3.2 Schedules调度Programs执行的Jobs

通过schedule 调度program 的执行的job。10g 版本中SCHEDULER 将JOB分成了多个部分,program 负责做什么,schedule 负责啥时候做,job 就简单了,一个字:做。

前面几个小节,已经分别演示了创建管理Jobs,创建管理Programs 以及创建和管理Schedules,下面我

们通过实例来演示,如何创建通过schedule 调度program 的执行的job 吧。


1. 我们用前面创建的Program: IPCONFIG,执行操作系统命令ipconfig。

2. 用我们刚创建的schedule:MySchedule

3. 创建job,按照指定的schedule,执行program,操作如下:

SQL> BEGIN

DBMS_SCHEDULER.CREATE_JOB (

job_name => 'ExecCmd',

program_name => 'IPCONFIG',

schedule_name => 'MySchedule',

enabled => true);

END;

/

PL/SQL procedure successfully completed.

创建job 时,start_date,repeat_interval,job_action 等均无须指定,因为这些参数将由program 和schedule 来控

制。


这样,操作完成后,ORACLE 就会自动定时(当前设置为每周执行一次)program 中定义的操作。

要查看当前的执行情况,通过*_scheduler_job_run_details 即可查询(*_scheduler_job_log 也可以,不过该视

图中信息不如detail 中全面)。例如,查看刚刚创建的"ExecCmd"任务的执行情况,执行命令如下:

SQL> select log_id,log_date,status,additional_info from user_scheduler_job_run_details where job_name = 'ExecCmd';


3.3 设置Repeat Interval

Job 和Schedule 中REPEAT_INTERVAL 参数都是用来控制执行的频率或周期,虽然说周期是一个时间性概念,不过REPEAT_INTERVAL 指定的时候并不是一个时间值,而是由一组关键字描述的时间。

除了前面介绍Job 和Schedule 的REPEAT_INTERVAL 参数时,提到该参数拥有FREQ 以及INTERVAL 两个关键字,其实除此之外,还有如BYMONTH、BYWEEKNO、BYYEARDAY、BYDATE 等等参数,可以用来进行更精确的定义,比如通过BYMONTH 关键字指定调度运行的月份,BYDAY 指定调度在哪天运行等等。


REPEAT_INTERVAL 参数的详细语法如下:

repeat_interval = regular_schedule | combined_schedule

==============================

regular_schedule = frequency_clause

[";" interval_clause] [";" bymonth_clause] [";" byweekno_clause]

[";" byyearday_clause] [";" bydate_clause] [";" bymonthday_clause]

[";" byday_clause] [";" byhour_clause] [";" byminute_clause]

[";" bysecond_clause] [";" bysetpos_clause] [";" include_clause]

[";" exclude_clause] [";" intersect_clause][";" periods_clause]

[";" byperiod_clause]

==============================

combined_schedule = schedule_list [";" include_clause]

[";" exclude_clause] [";" intersect_clause]

frequency_clause = "FREQ" "=" ( predefined_frequency | user_defined_frequency )

predefined_frequency = "YEARLY" | "MONTHLY" | "WEEKLY" | "DAILY" |

"HOURLY" | "MINUTELY" | "SECONDLY"

user_defined_frequency = named_schedule

==============================

interval_clause = "INTERVAL" "=" intervalnum

intervalnum = 1 through 99

bymonth_clause = "BYMONTH" "=" monthlist

monthlist = monthday ( "," monthday)*

month = numeric_month | char_month

numeric_month = 1 | 2 | 3 ... 12

char_month = "JAN" | "FEB" | "MAR" | "APR" | "MAY" | "JUN" |

"JUL" | "AUG" | "SEP" | "OCT" | "NOV" | "DEC"

byweekno_clause = "BYWEEKNO" "=" weeknumber_list

weeknumber_list = weeknumber ( "," weeknumber)*

weeknumber = [minus] weekno

weekno = 1 through 53

byyearday_clause = "BYYEARDAY" "=" yearday_list

yearday_list = yearday ( "," yearday)*

yearday = [minus] yeardaynum

yeardaynum = 1 through 366

bydate_clause = "BYDATE" "=" date_list

date_list = date ( "," date)*

date = [YYYY]MMDD [ offset | span ]

bymonthday_clause = "BYMONTHDAY" "=" monthday_list

monthday_list = monthday ( "," monthday)*

monthday = [minus] monthdaynum

monthdaynum = 1 through 31

byday_clause = "BYDAY" "=" byday_list

byday_list = byday ( "," byday)*

byday = [weekdaynum] day

weekdaynum = [minus] daynum

daynum = 1 through 53 /* if frequency is yearly */

daynum = 1 through 5 /* if frequency is monthly */

day = "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT" | "SUN"

byhour_clause = "BYHOUR" "=" hour_list

hour_list = hour ( "," hour)*

hour = 0 through 23

byminute_clause = "BYMINUTE" "=" minute_list

minute_list = minute ( "," minute)*

minute = 0 through 59

bysecond_clause = "BYSECOND" "=" second_list

second_list = second ( "," second)*

second = 0 through 59

bysetpos_clause = "BYSETPOS" "=" setpos_list

setpos_list = setpos ("," setpos)*

setpos = [minus] setpos_num

setpos_num = 1 through 9999

==============================

include_clause = "INCLUDE" "=" schedule_list

exclude_clause = "EXCLUDE" "=" schedule_list

intersect_clause = "INTERSECT" "=" schedule_list

schedule_list = schedule_clause ("," schedule_clause)*

schedule_clause = named_schedule [ offset ]

named_schedule = [schema "."] schedule

periods_clause = "PERIODS" "=" periodnum

byperiod_clause = "BYPERIOD" "=" period_list

period_list = periodnum ("," periodnum)*

periodnum = 1 through 100

==============================

offset = ("+" | "-") ["OFFSET:"] duration_val

span = ("+" | "-" | "^") "SPAN:" duration_val

duration_val = dur-weeks | dur_days

dur_weeks = numofweeks "W"

dur_days = numofdays "D"

numofweeks = 1 through 53

numofdays = 1 through 376

minus = "-"

这个语法形式看起来复杂无比,其实实用起来很简单,之所以看起来复杂,是因为其功能太过灵活。


例如:设置任务仅在周5 的时候运行:

REPEAT_INTERVAL => 'FREQ=DAILY; BYDAY=FRI';

REPEAT_INTERVAL => 'FREQ=WEEKLY; BYDAY=FRI';

REPEAT_INTERVAL => 'FREQ=YEARLY; BYDAY=FRI';

上述三条语句虽然指定的关键字小有差异,不过功能相同。

设置任务隔一周运行一次,并且仅在周5 运行:

REPEAT_INTERVAL => 'FREQ=WEEKLY; INTERVAL=2; BYDAY=FRI';

设置任务在当月最后一天运行:

REPEAT_INTERVAL => 'FREQ=MONTHLY; BYMONTHDAY=-1';

设置任务在3 月10 日运行:

REPEAT_INTERVAL => 'FREQ=YEARLY; BYMONTH=MAR; BYMONTHDAY=10';

REPEAT_INTERVAL => 'FREQ=YEARLY; BYDATE=0310';

上述两条语句功能相同。

设置任务每10 隔天运行:

REPEAT_INTERVAL => 'FREQ=DAILY; INTERVAL=10';

设置任务在每天的下午4、5、6 点时运行:

REPEAT_INTERVAL => 'FREQ=DAILY; BYHOUR=16,17,18';

设置任务在每月29 日运行:

REPEAT_INTERVAL => 'FREQ=MONTHLY; BYMONTHDAY=29';

设置任务在每年的最后一个周5 运行:

REPEAT_INTERVAL => 'FREQ=YEARLY; BYDAY=-1FRI';

设置任务每隔50 个小时运行:

REPEAT_INTERVAL => 'FREQ=HOURLY; INTERVAL=50';

另外,你是否在怀念常规job 中设置interval 的简便,虽然功能较弱,但是设置操作非常简单,无须懊恼,

其实SCHEDULER 中的REPEAT_INTERVAL 也完全可以按照那种方式设置,前面都说了,REPEAT_INTERVAL

实际上是指定周期,直接指定一个时间值,当然也是周期喽。

比如说,设置任务每天执行一次,也可以设置REPEAT_INTERVAL 参数值如下:

REPEAT_INTERVAL => 'trunc(sysdate)+1'

又比如设置任务每周执行一次:

REPEAT_INTERVAL => 'trunc(sysdate)+7'、


不过需要注意,这种方式仅用于创建SCHEDULER 中jobs 时使用,不能用于schedule。


四、使用Events

Event 直译对应的中文解释是指事件,不过单纯讲事件毕竟太抽象了,举个示例来形容吧。A(对应某个应用

程序,或者是ORACLE 中的进程)在干活时突然眉头一皱说道,不好,前方有情况,这可怎么办!这时,只见

它认真想了想,过了一会儿脸上一喜说道:有了,俗话说早请示啊晚汇报,出现情况要找领导,赶紧给领导发

消息呗!于是B(也是对应某个应用或ORACLE 进程)就收到了一条A 发过来的"前方有XX 情况"的消息,这个

过程就叫EVENT(含A 发消息以及B 接收消息)。

SCHEDULER 中有两种触发EVENT 的情况:

   Scheduler 触发的Events

Scheduler 中触发的Events,一般是说当前schduler 中job 的状态发生修改,类似job 启动,或者运行结束,或者达到运行时间等诸如此类的动作,都能够抛出一个EVENT,接收到EVENT 的applicate 就可以根据这些信息进行适当的处理。

比如说,由于系统太过于繁忙,超出job 启动时间后30 分钟,job 仍然没能顺利启动,那么这个时候,Scheduler 就可以抛出一条EVENT 给外部的应用,以便外部应用能够及时通知DBA,进行处理。

   application 触发的Events

外部的应用也可以触发Events,并且由Scheduler 来接收并处理这一类型的Events。所谓Scheduler 处理EVENT 就是指Scheduler 启动相应的job 来执行相关操作,这类job 在创建时专门声明了event 的处理,这样当接收到EVENT 时,这类job 就会启动。

Scheduler 使用Oracle 高级队列来抛出以及销毁Events。当抛出Schduler 触发的Events 时,Scheduler 将消息入队到默认的event 队列,application 则通过检查该队列来处理Events。当抛出application 触发的Events 时,application 将消息入队到处理job 对应的队列中。


下面我们也按照这两个类型来介绍Scheduler 中的Events。


4.1 Scheduler抛出的Events

前面说了,Scheduler 抛出的Events 一般是指job 状态改变时触发的,那么是不是说只要job 状态发生了改变,就会触发Events,其实并非如此,因为默认情况下,job 是不触发Events 的。

Scheduler 中的job 有一个属性叫raise_events,专门用来设置job 触发Events 的条件,该属性在CREATE_JOB时不能执行,因此默认情况下该属性不会赋值,自然也就不会触发EVENT。要设置raise_events 属性,只能是在job 创建完成后,通过SET_ATTRIBUTE 过程修改job 的raise_events 属性。

例如,修改前面创建的job-,启用raise_events 属性,执行语句如下:

SQL> BEGIN

DBMS_SCHEDULER.SET_ATTRIBUTE('JOBTEST', 'raise_events',DBMS_SCHEDULER.JOB_ALL_EVENTS);

END;

/

PL/SQL procedure successfully completed.


上述示例中指定的raise_events 属性的属性值DBMS_SCHEDULER.JOB_ALL_EVENTS,就是抛出Events的触发条件。

触发Events 的有下列的类型,分别代表不同的操作:

   job_started:JOB 启动;

   job_succeeded:JOB 成功结束;

   job_failed:JOB 执行失败;

   job_broken:JOB 被置为BROKEN 状态;

   job_completed:JOB 达到最大运行次数,或者运行的结束日期;

   job_stopped:JOB 被STOP_JOB 过程置为停止执行的状态;

   job_sch_lim_reached:Job 的schedule 达到限定值;

   job_disabled:JOB 被置于DISABLE 状态;

   job_chain_stalled:运行于chain 的JOB 被置于CHAIN_STALLED 状态;

   job_all_events:含上述提到的所有类型;

   job_run_completed:由于Job 运行出错、成功结束或被手动停止。


起用raise_events 后,Scheduler 就会按照设定的触发条件,当达到触发条件时,即会抛出事件信息到SYS.SCHEDULER$_EVENT_QUEUE 队列。

例如,手动执行一次JOBTEST,看看是否向队列中记录信息,操作如下:

SQL> exec dbms_scheduler.run_job('JOBTEST');

PL/SQL procedure successfully completed.

执行下列脚本,出队数据:

SQL> set serveroutput on

SQL> DECLARE

l_dequeue_options DBMS_AQ.dequeue_options_t;

l_message_properties DBMS_AQ.message_properties_t;

l_message_handle RAW(16);

l_queue_msg sys.scheduler$_event_info;

BEGIN

l_dequeue_options.consumer_name := 'TEST';


DBMS_AQ.dequeue(queue_name => 'SYS.SCHEDULER$_EVENT_QUEUE',

dequeue_options => l_dequeue_options,

message_properties => l_message_properties,

payload => l_queue_msg,

msgid => l_message_handle);

COMMIT;


DBMS_OUTPUT.put_line('event_type : ' || l_queue_msg.event_type);

DBMS_OUTPUT.put_line('object_owner : ' || l_queue_msg.object_owner);

DBMS_OUTPUT.put_line('object_name : ' || l_queue_msg.object_name);

DBMS_OUTPUT.put_line('event_timestamp: ' || l_queue_msg.event_timestamp);

DBMS_OUTPUT.put_line('error_code : ' || l_queue_msg.error_code);

DBMS_OUTPUT.put_line('event_status : ' || l_queue_msg.event_status);

DBMS_OUTPUT.put_line('log_id : ' || l_queue_msg.log_id);

DBMS_OUTPUT.put_line('run_count : ' || l_queue_msg.run_count);

DBMS_OUTPUT.put_line('failure_count : ' || l_queue_msg.failure_count);

DBMS_OUTPUT.put_line('retry_count : ' || l_queue_msg.retry_count);

END;

/

event_type : JOB_STARTED

object_owner : TEST

object_name : INSERT_TEST_TBL

event_timestamp: 25-AUG-09 12.49.29.558758 PM +08:00

error_code : 0

event_status : 1

log_id :

run_count : 1

failure_count : 0

retry_count : 0

PL/SQL procedure successfully completed.


从返回的信息可以看到,event 的类型为JOB_STARTED,表示JOB 启动。实际上job:JOBTEST执行一次至少会向队列中插入两条event 信息,一条为JOB_STARTED,一条则为JOB_SUCCEEDED(也可能是JOB_FAILED),这里不详细演示,感兴趣的朋友不妨自行测试。

提示:SYS.SCHEDULER$_EVENT_QUEUE 队列基于SYS.SCHEDULER$_EVENT_QTAB 队列表,因此查询

SYS.SCHEDULER$_EVENT_QTAB 也可以获取上述的信息。

SYS.SCHEDULER$_EVENT_QUEUE 是一个固定队列,实际应用的过程中,DBA 应该根据实际情况,将该表访问权限授予相关用户,以便顺利出队该队列中的events 信息。

另外,友情提醒,默认情况下Scheduler 仅保留最近24 小时的Events 信息,如果希望修改该设置的话,可

以通过SET_SCHEDULER_ATTRIBUTE 过程,修改scheduler 的event_expiry_time 属性,该项属性的属性值以

秒为单位。


4.2 Application抛出的Events

首先要说明,这里所说的Application 是个代词,即可以表示ORACLE 数据库之外的应用程序,也可以是ORACLE 数据库中的PROCEDURE 等对象,总之你就将其理解成用户自己创建的对象就好了。

Scheduler 能够抛出Events 让外部应用处理,外部的应用也可以抛出Events 让Scheduler 启动job 处理,不过并不是任何job 都能够对外部应用抛出的Events 做出响应,必须在创建jobs 时明确指定响应的事件。那么如何指定呢?依靠下列两个附加的参数:

  queue_spec:指定外部应用抛出的events 消息入队的队列名;

  event_condition:指定触发job 启动的条件,这一参数的参数值在设置时应当基于事件消息的自身属性,因为事件消息在入队时,消息的属性都是由application 定义的,因此在设置触发条件时,也应该根据这些属性值就行设置。


下面,我们就演示创建一个由event 触发启动的job,在此之前,首先需要进行一些准备工具,比如创建队列,由于队列需要基于一个队列表,因此在创建队列之前,首先要创建一个队列表,考虑到队列表需要依赖一个对象类型,因此在创建队列表之前,先得创建一个type.......复杂,具体的操作步骤如下:

SQL> create or replace type Test_type1 as object

2 (

3 event_type VARCHAR2(10),

4 object_owner VARCHAR2(30),

5 object_name VARCHAR2(30)

6 );

7 /

Type created.

SQL> begin

2 dbms_aqadm.create_queue_table(

3 queue_table => 'my_queue_tbl1',

4 queue_payload_type => 'Test_type1',

5 multiple_consumers => true);

6 end;

7 /

PL/SQL procedure successfully completed.

SQL> begin

2 dbms_aqadm.create_queue(

3 queue_name => 'event_t1',

4 queue_table => 'my_queue_tbl1');

5 end;

6 /

PL/SQL procedure successfully completed.

OK,准备工作完成,下面就来创建一个event 触发启动的job,创建脚本如下:

SQL> BEGIN

2 DBMS_SCHEDULER.CREATE_JOB (

3 job_name => 'EVENT_JOB_T1',

4 job_type => 'STORED_PROCEDURE',

5 job_action => 'SYSTEM.IT',

6 event_condition => 'tab.user_data.event_type = ''OP_INSERT''',

7 queue_spec => 'EVENT_T1',

8 enabled => TRUE);

9 END;

10 /

PL/SQL procedure successfully completed.


上述脚本仅做演示,因此创建的job 仍然执行IT 过程。


通过pl/sql 直接向event_t1 队列中添加消息的方式,触发job 的启动,具体操作如下。

首先要执行DBMS_AQADM.START_QUEUE 过程,将event_t1 置于允许入队和出队状态(默认情况下创建的队列是不允许出队和入队操作的),脚本如下:

SQL> exec dbms_aqadm.start_queue(queue_name => 'event_t1',enqueue => true,dequeue => true);

PL/SQL procedure successfully completed.

执行入队操作:

SQL> declare

v_Message Test_type1;

v_EnqueueOptions dbms_aq.enqueue_options_t;

v_MessageProperties dbms_aq.message_properties_t;

v_msg_handle raw(16);

begin

v_message := jss_type1('OP_SELECT', user, 'tmpObj');

dbms_aq.enqueue(queue_name => 'event_t1',

enqueue_options => v_enqueueOptions,

message_properties => v_messageproperties,

payload => v_message,

msgid => v_msg_handle);

commit;

end;

/

PL/SQL procedure successfully completed.


查询队列表中的数据:

SQL> select user_data from my_queue_tbl1;

USER_DATA(EVENT_TYPE, OBJECT_OWNER, OBJECT_NAME)

---------------------------------------------------------

JSS_TYPE1('OP_SELECT', 'TEST', 'tmpObj')

然后查询job

SQL> select to_char(created,'yyyy-mm-dd hh24:mi:ss') from jss_1;

TO_CHAR(CREATED,'YY

-------------------

2009-08-25 12:49:29

看起来jss_1 表中并未有新增加记录,似乎job 没有执行啊。这很正常,还记得咱们创建job 时指定的event_condition 条件吗:


6 event_condition => 'tab.user_data.event_type = ''OP_INSERT''',

没错,只有当event_type 为'OP_INSERT'时才会触发job 的执行,前面入队时指定的是OP_SELECT,当然

没有触发job 中指定的procedure 啦,下面再次执行入队操作:

SQL> declare

v_Message jss_type1;

v_EnqueueOptions dbms_aq.enqueue_options_t;

v_MessageProperties dbms_aq.message_properties_t;

v_msg_handle raw(16);

begin

v_message := jss_type1('OP_INSERT', user, 'tmpObj');

dbms_aq.enqueue(queue_name => 'event_t1',

enqueue_options => v_enqueueOptions,

message_properties => v_messageproperties,

payload => v_message,

msgid => v_msg_handle);

commit;


end;

/

再次查看jss_1 表看看:

SQL> select to_char(created,'yyyy-mm-dd hh24:mi:ss') from jss_1;

TO_CHAR(CREATED,'YY

-------------------

2009-08-25 12:49:29

2009-08-25 13:21:21

多了一条记录,说明job 已经被自动触发。

最后再补充一句,基于event 的job 不能通过DBMS_SCHEDULER.RUN_JOB 过程执行,否则会触发ORA-00942: table or view does not exist 错误。


五、使用Chains

CHAIN(注意不要敲成CHINA) 可以被视做一组Programs 的复合,举个简单的例子:运行PROGRAM:A 以及PROGRAM:B,如果成功的话继续运行PROGRAM:C,否则的话运行PROGRAM:D。Programs:A、B、C、D 以及执行的逻辑关系就构成了一个最简单的CHAIN。

关于CHAIN 的管理操作比较多,比如创建/删除/修改Chains,添加/修改/删除Chain Steps 等等。

5.1 创建Chains

5.1.1 创建CHAIN对象

创建CHAIN 使用DBMS_SCHEDULER.CREATE_CHAIN 过程,这个过程调用非常简单,因为需要指定的

参数极少,该过程的定义如下:

SQL> desc dbms_scheduler.create_chain;

Parameter Type Mode Default?

------------------- ---------------------- ---- --------

CHAIN_NAME VARCHAR2 IN

RULE_SET_NAME VARCHAR2 IN Y

EVALUATION_INTERVAL INTERVAL DAY TO SECOND IN Y

COMMENTS VARCHAR2 IN Y

在创建时,甚至可以简单到只指定一个CHAIN 的名称,其它均为空即可,例如:

SQL> exec dbms_scheduler.create_chain('my_chain1');

PL/SQL procedure successfully completed.

定义好的Chains,可以通过*_SCHEDULER_CHAINS 视图查看,例如:

SQL> select chain_name from user_scheduler_chains;

CHAIN_NAME

------------------------------

MY_CHAIN1

注意,不是说创建了CHAIN 就齐活,只有一个CHAIN 对象ORACLE 还是啥也干不了(当然啦,相信从上面执行的创建语句大家也看出来了),CHAIN 对象创建之后,要做的工作其实才刚刚开始。其后,还需要定义ChainSteps 以及Chain rules。

5.1.2 创建Chain Step

Chain Steps 就是用来指定CHAIN 执行的操作及执行步骤, 创建CHAIN STEP 是通过

DBMS_SCHEDULER.DEFINE_CHAIN_STEP 过程进行,例如,为刚刚创建的my_chain1 添加一个step,执行操作如下:

SQL> begin

DBMS_SCHEDULER.DEFINE_CHAIN_STEP (

chain_name => 'my_chain1',

step_name => 'my_step1',

program_name => 'p_p1');

end;

/

PL/SQL procedure successfully completed.

Chain Steps 即可以调用PROGRAM(注意是program,不是procedure,当然program 中可以定义执行procedure),也可以调用EVENT,甚至调用其它CHAIN(这就叫嵌套CHAIN)。

下面接着为my_chain1 添加两个step,操作如下:

SQL> begin

DBMS_SCHEDULER.DEFINE_CHAIN_STEP (

chain_name => 'my_chain1',

step_name => 'my_step2',

program_name => 'p_p2');

DBMS_SCHEDULER.DEFINE_CHAIN_STEP (

chain_name => 'my_chain1',

step_name => 'my_step3',

program_name => 'p_p3');

end;

/

PL/SQL procedure successfully completed.

要查询定义的Chain Steps,则是通过*_SCHEDULER_CHAIN_STEPS 视图,例如:

SQL> select chain_name,step_name,program_name from user_scheduler_chain_steps;

CHAIN_NAME STEP_NAME PROGRAM_NAME

-------------------- -------------------- --------------------

MY_CHAIN1 MY_STEP1 P_P1

MY_CHAIN1 MY_STEP2 P_P2

MY_CHAIN1 MY_STEP3 P_P3


5.1.3 创建Chain Rule

接下来,要为CHAIN 的运行定义规则。定义规则是使用DBMS_SCHEDULER.DEFINE_CHAIN_RULE 过程,Chain Rules 依赖于Chain Steps,每个CHAIN RULE 都拥有condition 和action 属性,当满足condition 时则执行action 中指定的step。

DBMS_SCHEDULER.DEFINE_CHAIN_RULE 过程的语法如下:

SQL> desc dbms_scheduler.define_chain_rule;

Parameter Type Mode Default?

---------- -------- ---- --------

CHAIN_NAME VARCHAR2 IN

CONDITION VARCHAR2 IN

ACTION VARCHAR2 IN

RULE_NAME VARCHAR2 IN Y

COMMENTS VARCHAR2 IN Y

CHAIN_NAME 就不说了,需要注意的是CONDITION 和ACTION 两个参数。在为condition 参数指定值时,其语法看起来稍稍复杂一些,或者说是灵活,condition 参数值支持下列的语法形式:

TRUE

FALSE

stepname [NOT] SUCCEEDED

stepname [NOT] FAILED

stepname [NOT] STOPPED

stepname [NOT] COMPLETED

stepname ERROR_CODE IN (integer, integer, integer ...)

stepname ERROR_CODE NOT IN (integer, integer, integer ...)

stepname ERROR_CODE = integer

stepname ERROR_CODE != integer

stepname ERROR_CODE <> integer

stepname ERROR_CODE > integer

stepname ERROR_CODE >= integer

stepname ERROR_CODE < integer

stepname ERROR_CODE <= integer

甚至于,还可以制定成下列逻辑语法:

expression AND expression

expression OR expression

NOT (expression)

比如说,我们希望条件为step1 成功运行,那么可以指定condition 参数值如下:

'step1 completed'

Action 参数相对简单一些,这个参数用来指定当满足condition 参数时,CHAIN 执行的操作。

例如,创建CHAIN RULE,首先执行my_step1,如果my_step1 成功执行的话,就继续执行my_step2,如

果my_step2 也成功执行的话,则结束该CHAIN,创建脚本如下:

SQL> BEGIN

DBMS_SCHEDULER.DEFINE_CHAIN_RULE (

chain_name => 'my_chain1',

condition => 'TRUE',

action => 'START my_step1',

rule_name => 'my_rule1');

DBMS_SCHEDULER.DEFINE_CHAIN_RULE (

chain_name => 'my_chain1',

condition => 'my_step1 completed',

action => 'START my_step2',

rule_name => 'my_rule2');

DBMS_SCHEDULER.DEFINE_CHAIN_RULE (

chain_name => 'my_chain1',

condition => 'my_step2 completed',

action => 'end 0',

rule_name => 'my_rule3');

END;

/

PL/SQL procedure successfully completed.


5.1.4 运行Chains

最后,来运行一下创建的my_chain1 吧,手动运行CHAIN 是通过DBMS_SCHEDULER.RUN_CHAIN 过程,

例如:

SQL> BEGIN

DBMS_SCHEDULER.RUN_CHAIN (

chain_name => 'my_chain1',

start_steps => 'my_step1');

END;

/

PL/SQL procedure successfully completed.

语句执行成功,下面需要查看一下执行的结果。我们之前定义的p_p1 等program 对象,实际上是调用procedure,向一个指定表jss_t2 中插入记录,这里直接查询一下该表,就知道执行情况了(在此之前,jss_t2 表为空):

SQL> select * from jss_t2;

TP DT

------------------------------ ------------

p_p1 inserted 03-SEP-09

p_p2 inserted 03-SEP-09

你看,jss_t2 表中有了两条记录,对应前面设置的CHAIN RULE,说明my_step1 和my_step2 均已正确执行。提示:Chains 在执行前,必须被置于enabled 状态,默认情况下刚刚创建的CHAIN 都是disabled 状态,要修改Chains 的状态,还是通过DBMS_SCHEDULER.ENABLE 和DBMS_SCHEDULER.DISABLE 两过程,这里就不演示了。手动执行的CHAIN 的话没有系统级的日志记录,因此如果希望看到详细执行情况的话,建议创建job 来执行CHAIN,例如:


SQL> BEGIN

DBMS_SCHEDULER.CREATE_JOB (

job_name => 'chain_job_1',

job_type => 'CHAIN',

job_action => 'my_chain1',

repeat_interval => 'freq=daily;interval=1',

enabled => TRUE);

END;

/

PL/SQL procedure successfully completed.

然后,dba 就可以通过定期观察*_scheduler_job_run_details 视图来确认chain 的执行情况了。


5.2 管理Chains

5.2.1 修改Chains属性

基本上碰到修改CHAIN 属性的机率不会太大,因此确实没啥可修改的,对于CHAIN 对象来说,能够修改的属性只有两个:evaluation_interval 和comments,这两个参数一般情况下甚至都不会进行设置。如果你碰到了确实需要修改的情况,没问题,DBMS_SCHEDULER.SET_ATTRIBUTE 过程还记的吧,没错,修改CHAIN 也是用它。例如:

SQL> select chain_name,comments from user_scheduler_chains;

CHAIN_NAME COMMENTS

-------------------- --------------------------

MY_CHAIN1

SQL> exec dbms_scheduler.set_attribute('my_chain1','comments','change it for a test!');

PL/SQL procedure successfully completed.

SQL> select chain_name,comments from user_scheduler_chains;

CHAIN_NAME COMMENTS

-------------------- --------------------------

MY_CHAIN1 change it for a test !


5.2.2 设置Chain Step运行属性

修改Chain Step 的运行属性就不能使用DBMS_SCHEDULER.SET_ATTRIBUTE 了,而是有专门的过程

DBMS_SCHEDULER.ALTER_CHAIN 处理,该过程的定义如下:

SQL> desc dbms_scheduler.alter_chain;

Parameter Type Mode Default?

---------- -------- ---- --------

CHAIN_NAME VARCHAR2 IN

STEP_NAME VARCHAR2 IN

ATTRIBUTE VARCHAR2 IN

VALUE BOOLEAN IN

前两个参数就不说了,ATTRIBUTE 参数用来指定STEP 的属性值,可设定的属性值有3 个,每个属性值都

有TRUE 和FALSE 两个选项,由VALUE 参数指定:

   PAUSE:设置该参数值为TRUE 时,当step 运行时,其运行状态就会变更为PAUSED;

   SKIP:设置该参数值为TRUE 时,当step 满足运行条件时,并不是执行step 中的program,而是直接跳过,注意当SKIP 参数值设置为TRUE,并且PAUSE 参数值也被设置为TRUE,那么将会以PAUSE 的状态优先;

   RESTART_ON_RECOVERY:设置该参数值为TRUE 时,如果由于数据库shutdown 导致step 被停止,那么当下次数据库启动时,step 会自动重新运行。

DBMS_SCHEDULER.ALTER_CHAIN 过程修改Chain Step 属性后,只有当下次运行时才会生效,如果要修改当前运行中Chain Step 的属性,也有一个专门的过程DBMS_SCHEDULER.ALTER_RUNNING_CHAIN 进行处理,该过程语法与DBMS_SCHEDULER.ALTER_CHAIN 一模一样,这里就不详细介绍了。


5.2.3 删除Chain Rules

Chain Rules 没有对应的修改方法,如果要修改某个Chain 的rule,只能首先删除不适当的rule,然后重新添加新rule(所谓添加,其实就是再重新定义一个rule)。

删除Chain Rule 有专门的过程DBMS_SCHEDULER.DROP_CHAIN_RULE,该过程语法如下:

SQL> desc dbms_scheduler.drop_chain_rule;

Parameter Type Mode Default?

---------- -------- ---- --------

CHAIN_NAME VARCHAR2 IN

RULE_NAME VARCHAR2 IN

FORCE BOOLEAN IN Y

举个简单的示例,比如删除前面定义的my_rule3,执行过程如下:

SQL> exec dbms_scheduler.drop_chain_rule('my_chain1','my_rule3',true);

PL/SQL procedure successfully completed.


5.2.4 删除Chain Steps

删除Chain Step 也有专门的过程DBMS_SCHEDULER.DROP_CHAIN_STEP 进行处理,该过程语法如下:

SQL> desc dbms_scheduler.drop_chain_step;

Parameter Type Mode Default?

---------- -------- ---- --------

CHAIN_NAME VARCHAR2 IN

STEP_NAME VARCHAR2 IN

FORCE BOOLEAN IN Y

看着有点儿眼熟是吧,没错,与drop_chain_rule 的相似度高达90%以上。例如,删除之前定义的my_step3,

执行过程如下:

SQL> exec dbms_scheduler.drop_chain_step('my_chain1','my_step3',true);

PL/SQL procedure successfully completed.


5.2.5 删除Chains

如果要删除Chain 那就更简单了,执行dbms_scheduler.drop_chain 过程即可,例如:

SQL> exec dbms_scheduler.drop_chain('my_chain1',true);

PL/SQL procedure successfully completed.

注意,执行drop_chain 时,如果不指定force 参数为TRUE,那么默认情况下ORACLE 会首先检查要删除的CHAIN 是否还有被依赖的对象,如果存在的话,会报ORA-27479 错误,提示仍然有依赖的对象(所谓依赖的对象就是指,该chain 仍然存在chain_step 或chain_rule 之类),因此无法直接删除。这种情况下解决方案有两种:

一是手动删除所有相关的chain_step 和chain_rule,然后再执行chain 的删除,再就是附加force 参数并指定参数

值为true,这样ORACLE 就会自动替你清除所有依赖的对象了。

 

六、使用Job Classes

Job Classes 相当于创建了一个job 组,DBA 可以将那些具有相同特性的job,统统放到相同的Job Classes中,然后通过对Job Class 应用ORACLE 中的"资源使用计划"特性,就可以对这些job 执行过程中所需要的资源分配情况进行管理。

1、创建Job Classes

使用DBMS_SCHEDULER 包的CREATE_JOB_CLASS 过程创建Job Classes,该过程支持的参数如下:

SQL> desc dbms_scheduler.create_job_class;

Parameter Type Mode Default?

----------------------- -------------- ---- --------

JOB_CLASS_NAME VARCHAR2 IN

RESOURCE_CONSUMER_GROUP VARCHAR2 IN Y

SERVICE VARCHAR2 IN Y

LOGGING_LEVEL BINARY_INTEGER IN Y

LOG_HISTORY BINARY_INTEGER IN Y

COMMENTS VARCHAR2 IN Y

其中:

JOB_CLASS_NAME:要创建的Job Class 的名称,注意指定的长度不要超过30 个字符,也不要与现有Job Class 同名;

RESOURCE_CONSUMER_GROUP:指定创建的Job Class 所在的RCG;

提示:啥是Resource Consumer Group

你可以将其理解成一个资源分配的方式,处于相同RCG 组中的用户、会话、或者对象共用一组资源,这组资源中可供分配的资源按照DBA 指定的方式分配给RCG。如果设计合理,通过这种方式,可以更有效的利用服务器的资源。

SERVICE:指定创建的Job Class 所在Service,本选项常见于RAC 环境,我们都知道RAC 环境由多实例+数据库组成,此处所指定的Service 实际就是指Job Class 会在哪个实例上运行。

注意:本参数与RESOURCE_CONSUMER_GROUP 参数相互冲突,同一个Job Class 只同设置两个参数中的一个值。

   LOGGING_LEVEL:指定日志记录的级别,有下列三种级别:

   DBMS_SCHEDULER.LOGGING_OFF:关闭日志记录功能;

   DBMS_SCHEDULER.LOGGING_RUNS:对该Job Class 下所有任务的运行信息进行记录;

   DBMS_SCHEDULER.LOGGING_FULL:记录该Job Class 下任务的所有相关信息,不仅有任务的运行情况,甚至连任务的创建、修改等也均将记入日志。

   LOG_HISTORY:指定日志记录的时间,以天为单位,比如指定LOG_HISTORY 参数值为90,就表示日志信息保留最近90 天的内容。

   COMMENTS:指定注释信息。


上述各个参数,除了LOG_CLASS_NAME 参数为必选参外,其它均为可选参数,例如:

SQL> EXEC DBMS_SCHEDULER.CREATE_JOB_CLASS('my_first_jc');

PL/SQL procedure successfully completed


查询系统中已经存在的Job Classes .可以通过DBA_SCHEDULER_JOB_CLASSES视图( 或ALL_SCHEDULER_JOB_CLASS 视图)


当创建Jobs 时,可以通过JOB_CLASS 参数来指定job 所在的Job Class,如果不指定的话,创建的job 默认属于DEFAULT_JOB_CLASS 。至于说如何查询创建的jobs 属于哪个Job Class , 还用说吗,*_SCHEDULER_JOBS 视图中的JOB_CLASS 列呗。


2、管理Job Classes

DBMS_SCHEDULER.SET_ATTRIBUTE 过程大家应当还记的,前面的小节中演示中使用该过程,修改job的属性,实际上SET_ATTRIBUTE 也同样可以用来修改Job Class 的属性,操作方法与修改job 属性完全相同,只不过作用函概的范围更广,修改Job Class 后,该Job Class 下属的所有job 属性都会被级联修改(当前正运行的不会立刻生效,将等到下次运行时生效)。


例如:修改刚刚创建的MY_FIRST_JC 的日志保存时间:

SQL> EXEC DBMS_SCHEDULER.SET_ATTRIBUTE('SYS.MY_FIRST_JC','LOG_HISTORY','30');

PL/SQL procedure successfully completed.

提示:Job Class 可被修改的属性,即创建时可选择指定的那5 个属性。


3、删除Job Classes

DBMS_SCHEDULER 包提供了DROP_JOB_CLASS 过程,用来删除Job Classes。该过程调用非常简单,例

如,删除刚刚创建的MY_FIRST_JC,执行命令如下:

JSSWEB> EXEC DBMS_SCHEDULER.DROP_JOB_CLASS('MY_FIRST_JC');

PL/SQL procedure successfully completed.

如果有多个Job Classes 需要删除,并不需要多次执行DROP_JOB_CLASS,只需要在为该过程指定值时,参数值间以逗号分隔即可。

 

七、使用Windows

此Windows 非彼Windows,通常说的Windows 是指盖首富的操作系统,而此处所说的Windows,是指SCHEDULER 特性中的一个子项。在SCHEDULER 中,WINDOW 对应的是一个时间窗口的概念。我们知道普通的jobs 是没有运行时间管理地概念的,就是说一个job 启动之后,用户只能被动地等待其执行,一直到其执行地任务完成(或DBA 手动kill 对应进程),在此期间,执行的job 将与其它活动的进程共同竞争当前系统中的资源。对于大型数据库系统,系统资源那可是相当宝贵的无形资产哪,企能谁说用就用、想什么时候用就什么时候用,没点儿计划没点儿节制这还了得。你还别说,在9i 之前,还真就是这么回事儿,谁想用就用,谁也管不了,其中表示最甚的就是job。你是否想起了Job Classes,没错定义Job Classes 确实可以控制job 能够使用的资源,不过单单使用Job Classes 并不能灵活的控制job 在合适的时间使用适当的资源。进入10g之后,SCHEDULER 中提供了WINDOW,事情终于有了缓解。WINDOW 可以指定一个时间窗口,在此期间,通过与Job Classes 的搭配组合,能够有效控制job 执行时支配(使用)的资源。比如说job 通常是在凌晨服务器负载较低时执行,那么就可以通过WINDOW 设置在此期间,允许jobs 使用更多的系统资源,而到了工作时间后,如果job 仍未执行完成,为其分配另一个有限的资源,以尽可能降低job 执行占用的资源对其它业务的影响。


1、创建Window

创建Window 有一个专门的过程:DBMS_SCHEDULER.CREATE_WINDOW 进行处理,该过程有两种调用方式,如下:

--基于SCHEDULE

DBMS_SCHEDULER.CREATE_WINDOW (

window_name IN VARCHAR2,

resource_plan IN VARCHAR2,

schedule_name IN VARCHAR2,

duration IN INTERVAL DAY TO SECOND,

window_priority IN VARCHAR2 DEFAULT 'LOW',

comments IN VARCHAR2 DEFAULT NULL);

--基于定义的调度

DBMS_SCHEDULER.CREATE_WINDOW (

window_name IN VARCHAR2,

resource_plan IN VARCHAR2,

start_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,

repeat_interval IN VARCHAR2,

end_date IN TIMESTAMP WITH TIME ZONE DEFAULT NULL,

duration IN INTERVAL DAY TO SECOND,

window_priority IN VARCHAR2 DEFAULT 'LOW',

comments IN VARCHAR2 DEFAULT NULL);


刨开那些看着眼熟的,已经认识的,看参数名就知道其所代表含义的之外,下列几个参数可能需要关注:

   Resource_plan:这一参数用来指定要使用的资源使用计划,当打开WINDOW 时,就会自动按照指定的资源使用计划中的设置分配资源,当WINDOW 关闭时(没错,window 是会关闭的,要不怎么说有效控制资源的使用情况泥),系统会自动切换回适当资源计划。这个参数在执行过程时甚至可以指定为NULL 或空值'',当设置为NULL 时,就表示使用默认的资源计划,当设置为空值''时,表示禁用资源使用计划。

   Duration:指定WINDOW 的有效期,比如说指定为interval '5' hour 就表示5 个小时,该参数在执行过程时必须指定参数值,否则创建会报错。

   Window_priority:该参数用来指定WINDOW 的优先级。因为在相同时间只有一个WINDOW 有效,因此如果在创建WINDOW 时发现重叠的情况,ORACLE 就需要根据这一参数指定的规则,来确定优先级,说白了就是先把资源给谁用,这一参数有两个可选值:HIGH 或LOW,默认值为LOW。


正如前面CREATE_WINDOW 过程语法结构显示的那样,调用该过程有两种方式,差异就在于是指定现有定义好的调度SCHEDULE,还是在执行过程时指定调度,目标和实现的功能都是相同的,这里仅做示例,咱就挑个最复杂的方式吧,执行过程时指定调度,执行脚本如下:


SQL> begin

dbms_scheduler.create_window(

window_name => 'my_first_wd1',

resource_plan => null,

start_date => sysdate,

repeat_interval => 'FREQ=DAILY; INTERVAL=5',

duration => interval '1' hour);

end;

/

PL/SQL procedure successfully completed.


查询当前拥有的WINDOW,可以通过*_SCHEDULER_WINDOWS视图(注意只有DBA 和ALL,没有USER,因为所有定义的WINDOW 都属于SYS 用户)。除了*_SCHEDULER_WINDOWS 视图显示当前所有WINDOW外,还有:

     *_SCHEDULER_WINDOW_DETAILS 视图:显示WINDOW 的详细信息;

   *_SCHEDULER_WINDOW_LOG 视图:显示WINDOW 的日志,比如打开和关闭;


2、管理Window

通过前面那些SCHEDULER 对象的学习,相当大家已经了解了ORACLE SCHEDULER 中对象的特点,对于多数对象的管理,不外乎下列几种:

修改对象属性,使用SET_ATTRIBUTE 过程;

SQL> exec dbms_scheduler.set_attribute('sys.my_first_wd1','start_date',sysdate+1);

PL/SQL procedure successfully completed.


ENABLE 对象,使用ENABLE 过程;

SQL> exec dbms_scheduler.enable('sys.my_first_wd1');

PL/SQL procedure successfully completed.


DISABLE 对象,使用DISABLE 过程;

SQL> exec dbms_scheduler.disable('sys.my_first_wd1');

PL/SQL procedure successfully completed.


删除对象,使用DROP_WINDOW 过程;

SQL> exec dbms_scheduler.drop_window('sys.my_first_wd1');

PL/SQL procedure successfully completed.


除此之外呢,对于WINDOW 对象来说,由于其特殊作用,又有:手动打开WINDOW,使用OPEN_WINDOW 过程;

注意WINDOW 是依赖于其调度的,因此在手动打开WINDOW 时,必须为其指定duration 属性:

SQL> exec dbms_scheduler.open_window('sys.my_first_wd1',interval '1' hour);;

PL/SQL procedure successfully completed.

   手动关闭WINDOW,使用CLOSE_WINDOW 过程;

SQL> exec dbms_scheduler.close_window('sys.my_first_wd1');

PL/SQL procedure successfully completed.

关闭和打开WINDOW,都会记录日志,大家可以通过*_SCHEDULER_WINDOW_LOG 视图中获取这部分

信息。


3、关于WINDOWGROUP

除了WINDOW 外,还有一个与WINDOW 有关系的叫WINDOW GROUP,一个WINDOW GROUP 可能包含多个WINDOW。使用WINDOW GROUP 的本意是这样的,假如说某个job 执行的时间比较长,甚至全天24小时都在执行,对于这类job,单个WINDOW 很难有效调整其资源占用,这时间呢,就可以通过设置一个WINDOW GROUP,该WINDOW GROUP 中包含了多个WINDOW,每个WINDOW 分别负责不同时间点时的资源使用计划。

然后在创建JOB 时,指定schedule_name 参数为WINDOW GROUP 的名称(想不到SCHEDULE_NAME 还能指定为WINDOW GROUP 哪,其实何止WINDOW GROUP,还可以直接指定成WINDOW 哪),这样,就可以通过很简单的方式,将job 与window 联系在一起了。


WINDOW GROUP 的创建和管理与前面介绍的方式极其相似:

   创建,使用CREATE_WINDOW_GROUP 过程;

   删除,使用DROP_WINDOW_GROUP 过程;

   添加WINDOW 成员,使用ADD_WINDOW_GROUP_MEMBER 过程;

   删除WINDOW 成员,使用REMOVE_WINDOW_GROUP_MEMBER 过程;

   启用,使用ENABLE 过程;

   禁用,使用DISABLE 过程;

这些过程的调用方式也都非常简单,这里就不着重演示了,感兴趣的朋友不妨自行尝试。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/tianlesoftware/archive/2009/10/22/4715218.aspx

posted on 2012-07-26 12:49  Alex’Sky  阅读(3147)  评论(0编辑  收藏  举报