七.2 包
包是由存储在一起的相关对象组成的P L / S Q L结构。包有两个独立的部分,即说明部分和包体,这两部分独立地存储在数据字典
中。从本质上讲,包就是一个命名的声明部分。任何可以出现在块声明中的语句都可以在包中使用,这些语句包括过程,函数,游标,类型以及变量。把上述内容放入包中的好处是我们可以从其他P L / S Q L块中对其进行引用,因此包为P L / S Q L提供了全程变量。
帮助理解:包可以看成一个封装的类,函数、过程是类的执行方法,类型、变量、游标等是数据。和面向对象中的包的概念不同,请区分。
包分为两个部分:
1.包的说明
对包进行说明,其实就是类定义全局变量,外部可以调用在此处声明的东西,包括类型、变量、过程、函数等。
2.包体
对包的说明中定义的函数、过程等实现。也可以自定义实现相应的过程、函数(只能公内部使用,私有的方法)。
格式:包的说明:
CREATE [OR REPLACE] PACKAGE package_name{IS | AS}
type_definition|
procedure_specification|
function_specification|
variable_declaration|
exception_declaration|
cursor_declaration|
pragma_declaration
END [ package_name];
包体:
和包的说明类似,对过程、函数之类的定义都是在as后面,没有begin关键字。
如果使用begin关键字,begin和end之间存放包的初始化信息。(构造方法,可以不写,空的构造方法)
/*创建包Random的说明*/
create or replace package Random
AS
--定义一个过程
Procedure changeSeed(p_New_Seed in number);
--定义一个函数,返回1到32726之间的随机数
Function getRand return Number;
--定义一个函数,返回1到p_MaxVal 之间的随机数
Function getRandMax(p_MaxVal in number) Return Number;
END;
上面是一个名为Random的包的说明部分,包括一个过程和两个函数
/*创建包Random的body*/
create or replace package body Random As
v_Multiplier constant number := 22695477;
v_Increment constant number :=1;
v_Seed number :=1;
--实现说明中的ChangeSeed过程
procedure changeSeed(p_New_Seed in number)
as
begin
v_Seed :=p_New_Seed;
end changeSeed;
--实现说明中的getRand函数
function getRand return number
as
begin
v_Seed := mod(v_Multiplier*v_Seed + v_Increment,(2*32));
return bitand(v_Seed/(2*16),32767);
end getRand;
--实现说明中的getRandMax函数
function getRandMax(p_MaxVal in number) return number
as
begin
return Mod(getRand(),p_MaxVal)+1;
end getRandMax;
Begin
--初始化数据,构造函数
ChangeSeed(to_number(to_char(sysdate,'sssss')));
End Random;
上面是包Random的包体。包括对声明部分的过程函数的实现、以及初始化数据的函数(该部分可以省掉)。
包的作用域:
包头中声明的任何对象都是在其作用域中,并且可在其外部使用包名作为前缀对其进行引
用。例如,我们可以从下面的P L / S Q L块中调用对象Random. getRand :
BEGIN
Random.getRand ();
END;
上面的过程调用的格式与调用独立过程的格式完全一致,其唯一不同的地方是在被调用的过程名的前面使用了包名作为其前缀。打包的过程可以具有默认参数,并且这些参数可以通过按位置或按名称对应的方式进行调用,就象独立过程的参数的调用方式一样。
这样我们就可以通过包引入全局变量、全局方法(函数、过程)、私有变量、私有方法等概念。即在包的包体中定义的函数、过程、变量(在声明未定义)作用于只限于包内部可以调用。如果在包声明部分定义的函数、过程、变量我们可以认为是全局的,我们通过打点得方式调用!
七.3.系统触发器
最后一个PL/SQL 显式STATEMENT,和上面三种PL/SQl块一样具有定义,执行,异常等块,但是过程、函数、包是显式的调用运行,触发器是隐式的由DML(insert、update、delete)操作激发执行(系统触发器特殊)。故没法传入参数等信息。
触发器分为三种:
1.DML触发器,
在做DML操作以前或者之后,运行此代码。
格式:
CREATE OR REPLACE TRIGGER TriggerName
【BEFORE|AFTER】 INSERT 【OR DELETE OR UPDATE】 ON 【TABLENAME】
DECLARE
//声明部分
BEGIN
//执行部分
END TriggerName;
2.替代触发器
该触发器只能使用在视图上面。因为视图的一些DML操作会被禁止(数据修改超出定义的范围之外,会被禁止),故我们使用该类触发器来替代用户进行的更新操作
格式:
CREATE OR REPLACE TRIGGER TriggerName
【INSTEAD OF】 INSERT 【OR DELETE OR UPDATE】 ON 【TABLENAME】
DECLARE
//声明部分
BEGIN
//执行部分
END ClassesRoomsInsert;
3.系统触发器
该触发器在系统级别上定义,如系统的关闭、开启、表的创建等。
格式:
CREATE OR REPLACE TRIGGER TriggerName
【BEFORE|AFTER】 CREATE ON SCHEMA
BEGIN
//执行部分
/*
INSERT INTO ddl_creations (user_id, object_type, object_name,
object_owner, creation_date)
VALUES (USER, SYS.DICTIONARY_OBJ_TYPE, SYS.DICTIONARY_OBJ_NAME,
SYS.DICTIONARY_OBJ_OWNER, SYSDATE);
*/
END TriggerName;
我们着重要了解的是DML触发器。该类触发器又分为行级触发器和表级触发器。
行级触发器,定义在每行的DML操作,即针对每行的DML触发该PL/SQL块
格式:CREATE OR REPLACE TRIGGER TriggerName
【BEFORE|AFTER】 INSERT 【OR DELETE OR UPDATE】 ON 【TABLENAME】
【FOR EACH ROW】此处多了For each row,是于表级触发器不同的关键语句。
在行级触发器上,我们可以使用:new,:old变量获取变更前后的数据。
:old数据只读,没有写入功能以及必要性。
在before之前,我们可以对:new变量中的数据进行改变。如主键,我们通过before触发器,设置主键。
begin
SELECT sequence.NEXTVAL--序列值
INTO :new.【ID】--主键字段
FROM dual
END;
/*创建表级Before触发器*/
Create or replace trigger Trigger_Before_Update_Students
before update on Students
declare
begin
-- TriggerVar.v_Counter :=nvl(TriggerVar.v_Counter,0);
TriggerVar.v_Counter :=0;
insert into temp_table values(seq_trigger.nextval,'before statement:counter = '||TriggerVar.v_Counter);
TriggerVar.v_Counter :=TriggerVar.v_Counter +1;
end Trigger_Before_Update_Students;
/*创建表级After触发器*/
Create or replace trigger Trigger_After_Update_Students
after update on Students
begin
insert into temp_table values(seq_trigger.nextval,'after statement:counter = '||TriggerVar.v_Counter);
TriggerVar.v_Counter :=TriggerVar.v_Counter +1;
end Trigger_After_Update_Students;
八 最后一个概念 作业
啥叫作业呢?通俗点就是数据库系统背后进程执行的计划任务。就是我们定义在下面的某个时间点要执行什么动作。当到了这个时间点,有数据库系统自动执行。我们使用作业进行定义的数据统计(如生成报表的元数据等)、定期删除废数据等。。。。
/*添加作业*/
Declare
v_job number;
begin
dbms_job.submit(v_job,'ExecuteInterval;',sysdate,'sysdate+(90/(24*60*60))');
dbms_output.put_line(v_job);
commit;
end;
参数说明:
job =》number out类型,返回当前作业的作业号,系统唯一。
what =》varchar2必须要以单引号包含,以分号结束。如果有参数用双引号。'ExecuteInterval'为我们到时间点执行的函数或者过程
interval =》varchar2,由作业进程根据该字符串计算出next_date值,如果该值为null,该作业会自动删除。
/*删除作业的方式*/
/*1设置作业的next_date为null,作业进程会自动删除*/
/*2remove方法,参数:作业进程号*/
begin
dbms_job.remove(v_job);/*作业进程作业号,我们在添加的时候返回的。也可以在select * from dba_jobs查询*/
commit;
end;