1.包规范

2.包体


包规范定义

create or replace package emp_pkg as

type emp_tab is table of emp%ROWTYPE index by binary_integer;

type emprectyp is record(

emp_no number,

sal number

);

cursor desc_salary return emprectyp;

procedure hire_employee(p_empno, number,p_ename varchar2,p_job varchar2,p_mgr number,p_sal number,p_comm number,p_deptno number);

procedure fire_employee(p_emp_id number);

end emp_pkg;


包体定义

create or replace package body emp_package

as

cursor desc_salary return emprectyp is select empno,sal from emp order by sal desc;

procedure hire_employee(p_empno number,p_ename varchar2,p_job varchar2,p_mgr number,p_sal number,p_comm number,p_deptno number)

is

begin

insert into emp values(p_empno,p_ename,p_job,p_mgr,p_sal,p_comm,p_deptno);

end;

procedure fire_employee(p_emp_id number) is

begin

delete from emp where empno=emp_id;

end;

end emp_package;


1.模块化设计:通过将逻辑相关的类型,常量,变量,异常和子程序放到一个命名的PLSQL模块中,使得每一个包都容易理解,有助于模块化程序的开发。

2.规范化的程序设计:在基于包的应用程序设计,可以首先规划并在包规范中定义包需要提供的功能,即便当前并没有实现包体,也可以编译包规范部分,然后引用该包的存储子程序会被编译。

3.实现信息的隐藏:包规范中定义的常量,变量和异常及子程序等是公有的,可以被外部访问,可以规划将哪些内容公开给外部进行调用,如果不想对外公开,可以再包体中定义这些内容,这样就可以实现信息的隐藏。

4.提供了良好的性能体验:由于在首次打开包子程序时,整个包都会被加载到内存中,因而后续的调用只需要从内存中读取而不需要再次读取磁盘,提供较好的

性能


调用包组件

使用 包名.元素名 这样的形式

当包第一次被调用的时候,将进行初始化,比如将包从硬盘上调到内存中来,放到系统全局工作区的共享缓冲池中,包的运行状态则被放入用户全局区的会话存储区中。因此可以保证每个调用包的会话都拥有包的运行副本,当会话结束时,包的运行状态才会被释放,这也就是说,包从第一次调用被初始化一直到会话结束才释放其运行状态,因此包中的变量具有会话级的作用域,因而可以跨多个事务存储数据。


begin

emp_action_pkg.v_deptno:=30;

dbms_output.put_line(emp_action_pkg.getratisedsalary(7369));

end;


begin

emp_action_pkg.v_deptno:=50;

emp_action_pkg.newdept(45,'采纳部','佛山');

end;

begin

dbms_output.put_line(emp_action_pkg.v_deptno);

end;

包的这种特性使得开发人员可以在包中保存跨多个事务的数据,这大大方便了复杂的程序逻辑的中间数据的存储,而不用特意地用一个表来存储临时的数据

如果在定义包规范时,指定了编译提示SERIALLY_REUSABLE,则可以将包的运行状态保存在系统全局工作区,而不是用户全局区,这样每次调用包以后,包的运行状态就会被释放,这样再次调用包时,将重新开始包的状态。


内存的占用量与包的并发调用用户数成正比。



包的重编译

alter package emp_action_pkg compile body;

alter package emp_action_pkg compile package;

alter package emp_action_pkg compile specification;


查看包的源代码

select object_type 对象类型,object_name 对象名称,status 状态 from user_objects where object_type in('PACKAGE','PACKAGE BODY') order by object_type,status,object_name;


select line,text from user_source where name='EMP_ACTION_PKG' AND TYPE='PACKAGE' ORDER BY line;

select line,text from user_source where name='EMP_ACTION_PKG' AND TYPE='PACKAGE BODY' ORDER BY line;


包重载

包重载实际上就是对包中包含的子程序的重载,也就是说一个包里可以有多个名称相同但参数不同的过程或函数,在调用时,PLSQL将通过形式参数进行匹配以便找到不同的重载的子程序。


在包中重载子程序

create or replace package emp_action_pkg_overload is

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE,

p_loc    dept.loc%TYPE

);

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE

);

function getraisedsalary(p_empno emp.empno%TYPE)

return number;

function getraisedsalary(p_ename emp.ename%TYPE)

return number;

end emp_action_pkg_overload;


包含重载子程序的包体实现

create or replace package body emp_action_pkg_overload is

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE,

p_loc    dept.loc%TYPE

)

AS

v_deptcount number;

begin

select count(*) into v_deptcount from dept where deptno=p_deptno;

if v_deptcount>0

then

raise_application_error(-20002,'出现了相同的员工记录');

end if;

insert into dept(deptno,dname,loc) values(p_deptno,p_dname,p_loc);

end newdept;

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE

)

AS

v_deptcount number;

begin

select count(*) into v_deptcount from dept where deptno=p_deptno;

if v_deptcount>0

then

raise_application_error(-20002,'出现了相同的员工记录');

end if;

insert into dept(deptno,dname,loc)values (p_deptno,p_dname,'中国');

end newdept;

function getraisedsalary(p_empno emp.empno%TYPE)

return number

is

v_job emp.job%TYPE;

v_sal   emp.sal%TYPE;

v_salaryraise number(10,2);

begin

select job,sal into v_job,v_sal from emp where empno=p_empno;

cass v_job

when '职员' then

v_salaryratio:=1.09;

when '销售人员' then

v_salaryratio:=1.18;

when '经理' then

v_salaryratio:=1.11;

else

v_salaryratio:=1;

end case;

if v_salaryratio<>1;

then

return round(v_sal * v_salaryratio,2);

else

return v_sal;

end if;

exception

when no_data_found then

return 0;

end getraisedsalary;

function getraisedsalary(p_ename emp.ename%TYPE)

return number

is

v_job emp.job%TYPE;

v_sal emp.sal%TYPE;

v_salaryratio number(10,2);

begin

select job,sal into v_job,v_sal from emp where ename=p_ename;

case v_job

when '职员' then

v_salaryratio:=1.09;

when '销售人员' then

v_salaryratio:=1.18;

when '经理' then

v_salaryratio:=1.11;

else

v_salaryratio:=1;

end case;

if v_salaryratio<>1;

then

return round(v_sal * v_salaryratio,2);

else

return v_sal;

end if;

exception

when no_data_found then

return 0;

end getraisedsalary;

function checkdeptno(p_deptno dept.deptno%TYPE) return number

as

v_counter number(2);

begin

select count(*) into v_couonter from dept where deptno=p_deptno;

return v_counter;

end;

end emp_action_pkg_overload;


调用

declare

v_sal number(10,2);

begin

emp_action_pkg_overload.newdept(43,'样品部','京东');

emp_action_pkg_overload.newdept(44,'纸品部');

v_sal:=emp_action_pkg_overload.getraisedsalary(7369);

v_sal:=emp_action_pkg_overload.getraisedsalary('史密斯');

end;


下面的重载是无效的

1.function get(p_empno IN number) return number;

  function get(p_ename OUT number) return number;

如果参数的名称和参数的模式不同


2.function get(p_empno emp.empno%TYPE) return number;

  function get(p_ename emp.ename%TYPE) return varchar2;

如果要重载的两个函数只是在返回的类型上不同


3.function get(p_empno number) return number;

  function get(p_ename integer) return number;



包初始化

包可以包含持续整个会话期内的数据结构,因此很多开发人员会考虑在包中放一些比较复杂的数据结构。

create or replace package inittest is

type emp_typ is table of emp%ROWTYPE index by binary_integer;

cursor emp_cur return emp%ROWTYPE;

curr_time number;

emp_tab emp_typ;

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE,

p_loc    dept.loc%TYPE

);

function getraisedsalary(p_empno emp.empno%TYPE)

return number;

end inittest;


create or replace package body inittest is

row_counter number:=1;

cursor emp_cur return emp%ROWTYPE is

select * from emp order by sal desc;

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname dept.dname%TYPE,

p_loc   dept.loc%TYPE

)

as

begin

function getraisedsalary(p_empno emp.empno%TYPE)

return number is

begin

null;

end getraisedsalary;

begin

select to_number(to_char(sysdate,'SSSSS')) into curr_time from dual;

for emp_row in emp_cur loop

emp_tab(row_counter):=emp_row;

row_counter:=row_counter+1;

end loop;

exception

when others then

dbms_output.put_line('出现了异常');

end inittest;



包的纯度级别

正如自定义函数可以在sql语句中直接使用一样,包中的公共函数也可以在sql语句中直接使用。同样,如果要在sql中使用这些包中的公共函数,需要对公共函数的定义加以限制,比如公共函数不能包含DML语句,不能读写远程包的变量。这些可以通过包的纯度级别来进行限制。


WNDS:限制函数不能修改数据库数据,即禁止函数执行DML操作。

WNPS:限制函数不能修改包变量,即不能为包变量赋值

RNDS:用于限制函数不能读取数据库数据,也就是禁止执行select操作

RNPS:用于限制函数不能读取包变量,也就是不能将包变量赋值给其他变量.


要在包中使用包纯度级别,必须首先在包规范中定义函数,然后指定函数的纯度级别。

create or replace package puritytest is

type dept_typ is table of dept%ROWTYPE index by binary_integer;

dept_tab dept_typ;

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE,

p_loc    dept.loc%TYPE

);

function getraisedsalary(p_empno emp.empno%TYPE)

return number;

pragma restrict_references(newdept,WNPS);

pragma restrict_references(getraisedsalary,WNDS);

end puritytest;


如果在包体中违反了纯度级别,越过了,就会报错。


如果要编写可被SQL语句引用的包的公用函数,函数必须要符合WNDS,WNPS和RNPS这3个纯度级别。



包权限设置

包提供了AUTHID语句来设置权限,只能在包规范中定义权限,包内的单独子程序必须全部是调用者子程序或定义者子程序,而不能进行混合。

create or replace package emp_action_pkg

AUTHID current_user is

v_deptno number(3):=20;

procedure newdept(

p_deptno dept.deptno%TYPE,

p_dname  dept.dname%TYPE,

p_loc    dept.loc%TYPE

);

function getraisedsalary(p_empno emp.empno%TYPE)

return number;

end emp_action_pkg;


在包中使用游标

由于包的这种会话级别存储数据的特性,因此可以在包中定义游标。

1.在包规范中定义整个游标,包含查询语句,这与在本地PL/SQL块中定义游标非常相似。

2.仅定义一个游标头而不包含查询语句,在这种情况下,只有在包体中指定查询,隐藏了游标的实现细节

如果在包中仅定义游标头,那么必须要使用return子句来指定游标将提取的数据的元素类型,当然这个类型一般是有游标的select语句确定的记录类型。一般return子句定义如下两种类型:

1.使用%ROWTYPE属性定义的记录类型

2.用户自定义的记录类型


在包规范中声明游标

create or replace package emp_pkg as

type emp_tab is table of emp%ROWTYPE index by binary_integer;

type emprectyp is record(

emp_no number,

sal number

);

cursor desc_salary return emprectyp;

cursor emp_cur(p_deptno IN dept.deptno%TYPE) is

select * from emp where deptno=p_deptno;

procedure hire_employee(p_empno number,p_ename varchar2,p_job varchar2,p_mgr number,p_sal number,p_comm number,p_deptno number);

procedure fire_employee(p_emp_id number);

end emp_pkg;


包体中定义游标查询

create or replace package body emp_pkg

as

cursor desc_salary return emprectyp is

select empno,sal from emp order by sal desc;

procedure hire_employee(p_empno number,p_ename varchar2,p_job varchar2,p_mgr number,p_sal number,p_comm number,p_deptno number,p_hiredate DATE) is

begin

for emp_salrow in desc_salary loop

dbms_output.put_line(emp_salrow.emp_no||':'||emp_salrow.sal);

end loop;

end;

procedure fire_employee(p_emp_id number) is

begin

delete from emp where empno=p_emp_id;

for emp_row in emp_cur(20) loop

dbms_output.put_line(emp_row.empno||' '||emp_row.deptno);

end loop;

end;

end emp_pkg;


由于游标的作用并不局限于某个特定的PL/SQL块,因此可以先在某个子程序中打开一个打包的游标,然后不直接关闭,使它一直保持打开的状态以便由其他的包,块或子程序调用,最后另外关闭或退出oracle会话




检查包的依赖性


在oracle中,包头不依赖于包体,也就是说可以改变包体而不影响包头的对象。

由于包体要依赖包头,同时包体依赖于emp表,但是包头并不依赖于包体或emp表,因此如果对包体进行改变,那么并不影响包头的状态,包头并不需要重新翻译。但是如果包头发生了改变,将会使包体自动失效,因为包体紧密依赖包头。


1.sql 查询包 包规范 有效性

select object_name,object_type,status from user_objects where object_name IN('EMP_PKG_DEPENDENCY','EMP');



使用系统包

默认情况下,oracle并没有启用DBMS_OUTPUT包,也就是说,如果直接使用DBMS_OUTPUT.PUT_LINE或GET_LINE来输出或获取消息,是看不到任何输出的。因此在使用DBMS_OUTPUT包之前,总要先使用DBMS_OUTPUT.ENABLE来启用包。

DBMS_OUTPUT.ENABLE(buffer_size in integer default 20000);


如果先执行了set serveroutput on 语句,则没有必要使用该过程,因为set serveroutput on 会自动调用该语句。


关闭时 可以DBMS_OUTPUT.DISABLE   或者set serveroutput off


向缓冲区中提取与写入内容

必须要理解的是,DBMS_OUTPUT并不是直接将信息输出到屏幕,而是将数据写入到了一个缓冲区中,然后再次将这些输出读回。当在sql*plus中使用set serveroutput on 命令时,实际上是让sql*plus在每条dbms_output之后检查缓冲区中的内容,然后在屏幕上进行显示。


可以使用如下几个过程来向缓冲区中输入消息

PUT:将信息写入缓冲区,并不包含换行符

PUT_LINE:将完整的行信息写入缓冲区,包含换行符

NEW_LINE:在行尾添加换行符,在使用put时,必须调用NEW_LINE过程来添加换行符。


可以使用如下几个过程来从缓冲区中提取消息。

GET_LINE:用于取得缓冲区的单行信息

GET_LINES:用于取得缓冲区的多行信息


PUT/PUT_LINE---->将信息写到缓冲区----->DBMS_OUTPUT数据缓冲区<-------从缓冲区中提取数据进行显示<----GET_LINE/GET_LINES-------->屏幕



使用DBMS_PIPE包

DBMS_PIPE包用于在同一个oracle的instance,即例程的不同会话之间进行通信。管道非常类似于UNIX操作系统中的管理,但是oracle管道并不是使用像在UNIX中那样的操作系统调用的机制,其管道信息被缓存在系统全局区SGA中,当关闭oracle例程时,就会丢失管理信息。管道分为两种:


1.公用管道:是指所有数据库用户都可以访问的管道

2.私有管道:私有管道只能由建立管道的数据库用户访问


会话A:管道发送方 会话B:管道接收方

创建管道CREATE_PIPE

|

写信息到缓冲区 从接收缓冲区读取消息

PACK_MESSAGE PACK_MESSAGE

| |

向管道发送信息 从管道接收消息,存储到接收缓冲区

SEND_MESSAGE-------------------->RECEIVE_MESSAGE


1.创建管道CREATE_PIPE

DBMS_PIPE.CREATE_PIPE(pipename IN varchar2,maxpipesize IN integer default 8192,private IN boolean default true)

return integer;


status:=DBMS_PIPE.CREATE_PIPE('privatepipe',8192,true); 创建一个私有管道

status:=DBMS_PIPE.CREATE_PIPE('privatepipe',8192,false); 创建一个公有管道


2.缓存消息PACK_MESSAGE

为了给换到发送消息,必须要先使用过程PACK_MESSAGE将消息写入本地消息缓冲区,然后使用SEND_MESSAGE将本地消息缓冲区中的消息发送到管道。PACK_MESSAGE的功能就是将消息存到私有的信息缓冲区中,该过程具有多个重载过程,分别接收类型为varchar2,number或date类型的数据项。


DBMS_PIPE.PACK_MESSAGE(item IN varchar2);

DBMS_PIPE.PACK_MESSAGE(item IN nchar);

DBMS_PIPE.PACK_MESSAGE_RAW(item IN RAW);

DBMS_PIPE.PIPE_MESSAGE_ROWID(item IN ROWID);


在缓冲区中只能存放4096字节的数据,如果超过了限制,oracle将抛出异常


declare

v_ename emp.ename%TYPE;

v_sal emp.sal%TYPE;

v_rowid ROWID;

v_empno emp.empno%TYPE:=&empno;

begin

select rowid,ename,sal into v_rowid,v_rowid,v_sal from emp where empno=v_empno;

dbms_pipe.pack_message('员工编号:'||v_empno||'员工名称:'||v_ename);

dbms_pipe.pack_message('员工薪资:'||v_sal||'ROWID值:'||v_rowid);

end;



3.发送消息SEND_MESSAGE

declare

v_sendflag int;

begin

v_sendflag:=dbms_pipe.send_message('PUBLIC_PIPE');

if v_sendflag=0 then

dbms_output.put_line('消息成功发送到管道');

end if;

end;



4.接收消息RECEIVE_MESSAGE

receive_message函数用于接收管道信息,将接收到的消息写入本地消息缓冲区,然后删除管道中的消息,因此必须特别注意,管道消息只能被接收一次。

receive_message会在指定的管道不存在时,隐式地创建管道,并且等待接收信息。如果在指定时间内没有接收到消息,调用会返回同时管道自动删除。


declare

v_receiveflag int;

begin

v_receiveflag:=DBMS_PIPE.receive_message('PUBLIC_PIPE');

if v_receiveflag=0 then

dbms_output.put_line('成功的从管道中获取消息');

end if;

end;



5.确定数据类型NEXT_ITEM_TYPE

在调用了receive_message之后,可以使用NEXT_ITEM_TYPE来确定本地消息缓冲区的下一项的数据类型

dbms_pipe.next_item_type return integer;

函数返回值:

0表示无数据项 6表示number 9表示varchar2  11表示rowid  12表示date   23表示raw类型



6.读取缓冲区数据UNPACK_MESSAGE

该过程将从缓冲区中读取消息,和PACK_MESSAGE过程一样,该过程也是重载的,因此能够接收多种类型的参数。

dbms_pipe.unpack_message(item out number);

dbms_pipe.unpack_message_raw_rowid(item out rowid);

参数中的item表示从私有数据缓冲区中接收的数据所保存的变量,如果缓冲区没有数据或类型不匹配,则会触发ORA-06556或ORA-06559,因此最好在接收之前,使用NEXT_ITEM_TYPE函数确定缓冲区中下一个项目的数据类型


declare

v_message varchar2(100);

begin

dbms_pipe.unpack_message(v_message);

dbms_output.put_line(message);

end;


7.删除管道remove_pipe ,清除管道内容remove_pipe, 复位管道缓冲区reset_buffer


8.返回会话名称unique_session_name

该函数用来为特定会话返回唯一的名称,名称的最大长度为30个字节,对于同一会话来说,其值不会改变


管道例子

1.一个会话发送消息到管道时,需要首先将消息写入本地消息缓冲区,然后将本地消息缓冲区内容发送到管道

2.当接收管道消息时,需要首先使用本地消息缓冲区接收管道消息,然后从消息缓冲区取得具体消息


下面是创建2个过程,然后使用这两个过程在不同的会话中发送和接收消息


发送管道消息

create or replace procedure end_pipe_message(pipename varchar2,message varchar2)

is

flag int;

begin

flag:=dbms_pipe.create_pipe(pipename);    --创建管道

if flag=0 then --如果管道创建成功

dbms_pipe.pack_message(message); --将消息写到本地缓冲期区

flag:=dbms_pipe.send_message(pipename); --将本地缓冲区中的消息发送到管道

end if;

end;


从管道接收消息

create or replace procedure receive_pipe_message(pipename varchar2,message out varchar2)

is

flag int;

begin

flag:=dbms_pipe.receive_message(pipename);   --从管道中获得消息,保存到缓冲区

if flag=0 then

dbms_pipe.unpack_message(message); --从缓冲区读取消息

flag:=dbms_pipe.remove_pipe(pipename);  --移除管道

end if;

end;



使用 分别用scott 和userb  登录

scott

sql>exec send_pipe_message('pipe_demo','向管道中发送一条消息');

sql>grant execute on scott.receive_pipe_message to userb;

userb

sql>declare

v_message varchar2(100);

    begin

scott.receive_pipe_message('pipe_demo',v_message);

dbms_output.put_line(v_message);

    end;



使用DBMS_ALTER

报警是基于事务的,意味着除非能够报警的事务被提交,否则等待过程中的事务不会被报警。

dbms_alter用于生成并传递数据的报警消息,通过在代码中合理地使用该包,可以发生特定数据库事件时将信息传递给应用程序。


发布报警SINGAL------>报警注册REGISTER

|

    报警等待(轮询)WAITONE或WAITANY

|

    删除报警REMOVE


1.注册报警事件REGISTER

该过程允许注册一个感兴趣的报警,名称以参数输入的形式告之,在一个会话中可以注册多个报警。如果以后不再需要报警,可以使用remove过程从报警注册表中移除

dbms_alter.register(name in varchar2);


2.等待特定报警waitone

该过程用于等待当前会话中特定的报警事件,并且在发生报警事件时输出报警消息,该过程在执行之前,会隐含地发出commit

dbms_alter.waitone(

name in varchar2,

message out varchar2,

status out integer,

timeout in number default maxwait

);


3.等待任意报警waitany

waitany过程会等待会话中已经注册的任何报警的发生,在这个过程执行前会有一个隐含的commit被执行,同样,在等待这个报警发生的同时,还可以首先发布某些报警。


dbms_alter.waitany(

name out varchar2,

message out varchar2,

status out integer,

timeout in number default maxwait

);


4.删除报警remove

5.删除所有报警removeall

6.时间间隔设置set_defaults

7.设置报警消息signal

该过程用于指定报警事件所对应的报警消息,只有在提交事务时才会发出报警消息,而当回退事务时是不会发出报警消息的.

dbms_alter.signal(name in varchar2,message in varchar2);

message指定报警消息 不能超过1800字节

多个会话可以并发执行同一个报警,每个会话发布报警时会阻塞其他会话发布的报警,直到报警被提交,因此事务会以序列的方式发生


演示

同样需要2个会话,在第一个会话创建如下匿名块

declare

v_altername varchar2(30):='alert_demo';

v_status integer;

v_msg varchar2(200);

begin

dbms_alter.register(v_alertname);

dbms_alter.waitone(v_alertname,v_msg,v_status);

if v_status !=0

then

dbms_output.put_line('error');

end if;

dbms_output.put_line(v_msg);

end;

上面代码注册了了一个名为alert_demo的报警,然后调用waiton等待报警,如果在sql*plus 执行这段代码,可以看到整个界面被阻塞了,用来等待有报警产生。


产生报警示例

declare

v_alertname varchar2(30):='alert_demo';

begin

dbms_alert.signal(v_alertname,'这是一个报警信息');

commit;

end;



使用DBMS_JOB包

必须确保设置了初始化参数JOB_QUEUE_PROCESSES的值不为0,才能使用DBMS_JOB对作业进行管理.


1.创建作业submit

dbms_ddl.analyze_object方法可以分析数据表,并且将存储结果存储起来,对于sql的运行效率有较大提升。


定义一个作业

declare

v_jobno number;

begin

dbms_job.submit

(v_jobno,'dbms_ddl.analyze_object(''TABLE'',''SCOTT'',''EMP'',''COMPUTE'');',SYSDATE,'SYSDATE+1');

dbms_output.put_line('获取的作业编号为:'||v_jobno);

commit;

end;

数据字典查询作业

select job,next_date,next_sec,interval,what from user_objects where job=22904;


sysdate+1:表示下一天的当前时间

trunc(sysdate)+1:表示下一天的午时,即12点

trunc(sysdate)+17/24:表示今天的17/24天,也就是从午夜0点以来的17个小时后,即下午5点开始执行。

'null':表示作业立即运行



2.移除作业remove

3.更改作业change

4.更改作业执行what

EXEC dbms_job.what(22904,'emp_pkg_dependency.fire_employee(7369)');


5.更改运行日期NEXT_DATE

6.数据库实例配置instance

7.更改间隔interval

8.中断作业BROKEN

9.强制作业运行RUN

使用RUN过程可以强制作业立即执行,即使作业已经标记为中断,也会强制执行,因为作业在运行完后会计算下一次执行的时间,因此在调用RUN过程之后,下一次的执行时间也会发生改变。