Oracle--存储过程

前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除

存储过程

命名的PL/SQL块可以被独立编译并存储在数据库中,Oracle提供了4种可以存储的PL/SQL块,及过程、函数、触发器和包

存储过程是命名的PL/SQL块,可有无参数、输入参数、输出参数或既输入又输出的参数,但通常无返回值,它保存在数据库中,不能被SQL语句直接执行或调用,需通过EXECUTE命令或在PL/SQL块内部调用,因是编译好的代码,执行效率高。

一、创建存储过程

创建存储过程与编写普通PL/SQL块虽都有声明、执行和异常处理三部分,但细节有别:存储过程需用PROCEDURE关键字后跟过程名和参数列表,且用CREATE或REPLACE关键字而非DECLARE,其格式:

sql 复制代码
 CREATE [OR REPLACE] PROCEDURE pro_name [(parameter1[,parameter2...)] IS|AS
 BEGIN
     plsql_sentences;
 [EXCEPTION]
     [dowith _ sentences;]
 END [pro_namel;

二、存储过程的参数

存储过程通过参数实现单元输入和输出数据,参数模式有IN(输入)、OUT(输出)和IN OUT(输入输出)三种,这增强了其灵活性,区别于前面创建的无参数的简单存储过程。

1、IN模式参数

IN模式参数是一种输入类型的参数,参数值由调用方传入,并且只能被存储过程读取。这种参数模式是最常用的,也是默认的参数模式,关键字IN位于参数名称之后。

sql 复制代码
 --创建一个存储过程,并定义3个IN模式的变量,然后将这3个变量的值插入dept表中
 create or replace procedure insert_dept(
     num_deptno in number,           --定义IN模式的变量,它存储部门编号
     var_dname in varchar2,          --定义IN模式的变量,它存储部门名称
     var_loc in varchar2) is         --定义IN模式的变量,它存储部门位置
 begin 
     insert into dept
     values(num_deptno,var_dname,var_loc);
     commit;     
 end insert_dept;
 /

上述代码成功创建了一个存储过程,需注意++参数类型不能指定长度++。调用或执行IN模式存储过程时,用户需传递若干参数值,以确保执行部分(BEGIN部分)有具体数值参与数据操作。向存储过程传入参数有以下3种方式。

1.1、指定名称传递

指定名称传递是在向存储过程传递参数时,需明确写出参数名称,左侧是参数名,中间用"=>"连接,右侧是参数值,其语法:

sql 复制代码
 pro_name(paramater1=>value1[,paramater2=>value2]...)

参数名称与存储过程中定义的参数顺序无关

sql 复制代码
 --示例:在PL/SQL块中调用存储过程insert_dept,然后使用"指定名称"的方式传入参数值,最后执行当前的PL/SQL块
 begin
     insert_dept(var_dname=>'财务部',var_loc=>'烟台',num_deptn=>15);
 end;
 /
1.2、按位置传递

指定名称传递参数虽然直观易读,但当参数过多时会使代码冗长,反而不利于阅读。此时可以采用按位置传递参数的方式,这种方式要求提供的参数值顺序必须与存储过程中定义的参数顺序相同

sql 复制代码
 --示例:在PL/SQL块中调用存储过程insert_dept,然后使用"按位置传递"的方式传入参数值,最后执行
 begin
     insert_into(11,'工程部','威海');
 end;
 /

当参数过多难以记住顺序和类型时,可通过DESC命令查看存储过程的参数定义信息,包括参数名、定义顺序、类型和模式等。

1.3、混合方式传递

混合方式就是将前两种方式结合到一起,可以兼顾二者的优点

sql 复制代码
 --示例:使用混合方式传递
 exec insert_dept(12,var_loc=>'济南',var_dname=>'测试部');

注意:在某个位置使用指定名称传递方式传入参数值后,其后面的参数值也要使用指定名称传递,因为指定名称传递方式有可能已经破坏了参数原始的定义顺序

2、OUT模式参数

OUT模式参数是一种输出类型的参数,表示这个参数在存储过程中已经被赋值,并且这个参数值可以被传递到当前存储过程以外的环境中,关键字OUT位于参数名称之后

sql 复制代码
 --创建一个存储过程,要求定义两个OUT模式的字符类型的参数,将在dept表中检索到的一行部门信息存储到这两个参数中
 create or replace procedure select_dept(
     num_deptno in number,
     var_dname out dept.dname%type,
     var_loc out dept.loc%type) is
 begin
     select dname,loc
     into var_dname,var_loc
     from dept
     where deptno = num_deptno;      --检索某个部门编号的部门信息
 exception
     when no_data_found then
         dbms_output.put_line('该部门编号不存在');
 end select_dept;
 /

在上述存储过程(即select dept)中,定义了两个OUT参数。由于存储过程通过OUT参数返回值,所以在调用或执行该存储过程时,需要定义变量来保存这两个OUT参数的值。

2.1、在PL/SQL块中调用OUT模式的存储过程

这种方式需要在PL/SQL块的DECLARE部分定义与存储过程中OUT参数兼容的若干变量

sql 复制代码
 --在PL/SQL块中声明若干变量,然后调用select_dept存储过程,并将定义的变量传入该存储过程中,以便接收OUT参数的返回值
 set serveroutput on
 declare
     var_dname dept.dname%type;  --声明变量,对应过程中的OUT模式的var_dname
     var_loc dept.loc%type;      --声明变量,对应过程中的OUT模式的var_loc
 begin
     select_dept(77,var_dname,var_loc);
     dbms_output.put_line(var_dname||'位于:'||var_loc);    --输出部门信息
 end;
 /
2.2、使用EXEC命令执行OUT模式的存储过程

当使用EXEC命令时,雲要在SQL*Plus环境中使用VARIABLE关键字声明两个变量,用以存储OUT参数的返回值

sql 复制代码
 --使用VARIABLE关键字声明两个变量,分别存储部门名称和位置信息,然后使用EXEC命令执行存储过程,并传入声明的两个变量来接受OUT参数的返回值
 variable var_dname varchar2(50);
 variable var_loc varcahr2(50);
 exec select_dept(11,:var_dname,:var_loc);
 ​
 --使用SELECT语句检索并输出变量var_dname和var_loc的值
 select :var_dname,:var_loc from dual;

注意:如果在存储过程中声明了OUT模式的参数,则在执行存储过程时,必须为OUT参数提供变量,以便接收OUT参数的返回值;否则,程序执行后将出现错误

3、IN OUT模式参数

在执行存储过程时,IN参数只能提供数据,不能被修改;OUT参数只能被赋值,不能提供数据;而IN OUT参数兼具两者特点,既能传入值也能返回值

sql 复制代码
 --创建一个存储过程,其中定义一个IN OUT参数,该存储过程用来计算这个参数的平方或平方根:
 create or replace procedure pro_square(
     num in out number,
     flag in boolean) is
     i int := 2;
 begin
     if flag then
         num := power(num,i)
      else
         num :=sqrt(num);
      end if
 end;

在上述存储过程中,定义了一个IN OUT参数,它在存储过程被调用时接收一个数值,之后与另一个IN参数配合来判断执行的运算方式(平方或平方根),最终将计算结果(平方或平方根的值)保存到该IN OUT参数中。

sql 复制代码
 --调用存储过程pro_square,计算某个数的平方或平方根
 set serverout on
 declare
     var_number number;      --存储要进行运行的值和运行后的结果
     var_temp number;        --存储要进行运算的值
     boo_flag boolen;        --平方或平方根的逻辑标记
 begin
     var_temp :=9;           --赋值变量
     var_number :=var_temp;
     boo_flag := false;
     pro_square(var_number,boo_flag);    --调用存储过程
     if boo_flag then
         dbms_output.put_line(var_temp ||'的平方是:'||var_number);
     else
         dbms_output.put_line(var_temp ||'的平方根是:'||var_number);
     end if;
 end;
 /

三、IN参数的默认值

IN参数的值是在调用存储过程中传入的,Oracle支持在声明IN参数时为其初始化默认值。如果在调用存储过程时未传入值,存储过程就会使用默认值进行操作。

sql 复制代码
 --创建一个存储过程,定义3个IN参数,并将其中的两个参数各设置一个初始默认值,然后将这3个参数的值插入dept表中
 create or replace procedure insert_dept(
     num_deptno in number,
     var_dname in varchar2 default '行政部',
     var_loc in varchar2 defailt '青岛') is
 begin
     insert into dept values(num_deptno,var_dname,var_loc);
 end;

在上述存储过程中,IN参数var_dname和var_loc都有默认值。因此,在调用insert_dept存储过程时,可以选择不传入值,直接使用其默认值(当然也可以传入值)。建议使用"指定名称传递"的方式传值,这样可以避免因参数顺序不固定而导致的混乱。

sql 复制代码
 --在PL/SQL块中调用insert_dept存储过程,并且只向该存储过程中传入两个参数值
 set serverout on
 declare
     row_dept dept%rowtype;      --定义行变量,与dept表的一行类型相同
 begin
     insert_dept(57,var_loc=>'太原');  --调用insert_dept存储过程,传入参数
     commit;                         --提交数据库
     select * into row_dept from dept where deptno=57;
     dbms_output.put_line('部门名称是:《'||row_dept.dname||'》,位置是:《'||row_dept.loc||'》');
 end;
 /

四、删除存储过程

当一个过程不再被需要时,要将此过程从内存中删除,以释放相应的内存空间,代码如下:

sql 复制代码
 DROP PROCEDURE count_num;

删除存储过程insert dept,代码如下

sql 复制代码
 DROP PROCEDURE insert_dept;

当一个存储过程已经过时,想重新定义时,不必先删除再创建,只需在CREATE语句后面加上OR REPLACE关键字即可:

sql 复制代码
 CREATE OR REPLACE PROCEDURE count_num

五、函数

函数通常用于计算并返回一个值,可将常用的计算或功能封装成函数。函数调用是表达式的一部分,而过程调用是一条PL/SQL语句。二者在创建形式上类似,都是编译后存储在内存中供用户使用。不过,调用函数时需要通过表达式,而调用过程只需使用过程名。此外,函数必须有一个返回值,而过程没有返回值。

1、创建函数

函数的创建语法与存储过程类似,是一种存储在数据库中的命名程序块。它可以接收零个或多个输入参数,但必须有返回值(这是与存储过程的不同之处)。定义函数格式:

sql 复制代码
 CREATE  [OR REPLACE] FUNCTION fun_name[(parameter1[,parameter2]...) RETURN data_type IS [inner_variable]
 BEGIN
     plsql sentence;
 [EXCEPTION]
     [dowith _ sentences;]
 END [fun_name];

由于函数有返回值,因此在函数主体部分(即BEGIN部分)必须使用返回语句返回函数值,并且要求返回值的类型要与函数声明时的返回值类型(即data_type)相同

sql 复制代码
 --定义一个函数,用于计算emp表中指定部门的平均工资
 create or replace function get_avg_pay(num_deptno number) return number is
     num_avg_pay number;     --保存平均工资的内部变量
 begin
     select avg(sal) into num_avg_pay from emp where deptno=num_deptno;
     return(round(num_avg_pay,2));   --返回平均工资
 exception
     when no_data_found then     --若部门编号不存在
         dbms_output.put_line('该部门编号不存在')
         return(0);
 end;

2、调用函数

函数声明时所定义的参数成为形式参数,应用程序调用时为函数传递的参数称为实际参数。实际参数和形式参数之间的数据传递有两种方法:传址法和传值法

调用函数和调用过程有所不同,需要一个变量来保存返回的值,这样函数就组成了表达式的一部分。因此不能像调用过程那样独立地调用函数。

3、删除函数

对于不再使用的函数,可以使用DROP FUNCTION语句进行删除,语法如下:

sql 复制代码
 DROP FUNCTION <function_name>;

六、触发器

触发器是一种特殊的存储过程,它定义了在数据库相关事件(如INSERT、UPDATE、CREATE等)发生时应执行的功能代码块,通常用于管理复杂的完整性约束、监控表的修改、通知其他程序,甚至实现数据审计功能。

1、触发器简介

触发器 是通过触发事件来执行的,与存储过程由用户或应用程序调用不同。触发事件包括执行DML语句 (INSERT、UPDATE、DELETE)、执行DDL语句 (CREATE、ALTER、DROP)、引发数据库系统事件 (如启动、退出、异常错误)以及引发用户事件(如登录、退出数据库操作),这些操作都能引起触发器运行。其格式:

sql 复制代码
 CREATE [OR REPLACE] TRIGGER tri_name
 [BEFORE | AFTER | INSTEAD OF] tri_event
 ON table_name | view_name | user_name | db_name
 [FOR EACH ROW [WHEN tri_condition]
 BEGIN
 plsql_sentences;
 END tri_name;
  • TRIGGER:创建触发器的关键字,类似于存储过程的PROCEDURE。
  • 触发时机的关键字有BEFORE(执行DML操作前触发,便于回滚或实现业务规则)、AFTER(DML操作后触发,便于记录操作或事后处理)和INSTEAD OF(替代触发器)。
  • ON用于指定操作的数据表、视图、用户模式或数据库,当对它们执行操作时触发器会运行。
  • FOR EACH ROW表示行级触发器,每行数据操作都会触发;否则为语句级触发器,无论影响多少行只执行一次。
  • tri_name是触发器名称,可通过OR REPLACE覆盖同名旧触发器。
  • trig_event是触发事件,如INSERT、UPDATE、DELETE等。
  • table_name|view_name|user_name|dbname分别表示操作对象。WHEN condition是触发条件子句,只有条件为TRUE时,遇到触发事件才会执行触发器。
  • plsql_sentences是实现触发器功能的主体代码

Oracle支持的触发器分为五种类型

  • 行级触发器,对每一行数据操作都会触发;
  • 语句级触发器,无论影响多少行,只执行一次;
  • 替换触发器,定义在视图上,替代实际语句;
  • 用户事件触发器,与DDL操作或用户登录、退出等事件相关;
  • 系统事件触发器,在Oracle系统事件(如实例启动与关闭)中触发。

2、语句级触发器

语句级触发器,是针对一条DML语句引起的触发器执行。在语句级触发器中,不使用FOR EACH ROW子句,也就是说无论数据操作影响多少行,触发器都只会执行一次。

在scot模式下,为了监控dept表的各种操作,需要创建一个日志表deptlog,用于存储对dept表的操作信息,如操作种类(插入、修改、删除)和操作时间。在scot模式下创建deptlog表,并定义两个字段分别存储操作种类和操作日期,代码:

sql 复制代码
 create table dept_log
 (
     operate_tag varchar2(10),       --定义字段,存储操作种类信息
     operate_time date               --定义字段,存储操作日期
 );
sql 复制代码
 --创建一个触发器tii_dept,该触发器在INSERT、UPDATE和DELETE事件下都可以被触发,操作的数据对象是dept表,并且在触发器执行时输出对dept所做的具体操作,代码如下:
 create or replace trigger tri_dept
     before insert or update or delete
     on dept     --创建触发器,当dept表发生插入、修改、删除操作时,将引起该触发器执行
 declare
     var_tag varchar2(10);
 begin
     if inserting then
         var_tag = '插入';
     elsif updating then
         var_tag = '修改';
     elsif deleting then
         var_tag = '删除';
     end if;
     insert into dept_log
     values(var_tag,sysdate);
 end tri_dept;
 /
sql 复制代码
 /*在数据表dept中实现插入、修改、删除3种操作,以便引起触发器tri_dept的执行,代码如下:*/
 insert into dept values(55,'业务部''上海');
 update dept set loc='杭州' where deptno=55;
 delete from dept where deptno=55;
 commit;
 select * from dept_log;

3、行级触发器

行级触发器会针对DML操作所影响的每一行数据都执行一次触发器 ,创建这种触发器时,必须在语法中使用FOR EACH ROW这个选项;使用行级触发器的一个典型应用就是给数据表生成主键值。

sql 复制代码
 --首先创建一个带有主键列的数据表
 create table goods
 (
     id int primary key,
     good_name varchar2(50)
 );

为了给goods表的id列生成不能重复的有序值,这里需要创建一个序列,使用CREATE SEQUENCE语句创建一个序列,命名为seq_id:

sql 复制代码
 create sequence seq_id;

上述代码创建了序列seqid,用户可在PL/SQL程序中调用其NEXTVAL属性获取有序数值,这些数值可作为goods表的主键值。接下来创建一个行级触发器,当向goods表插入数据时触发,并在触发器主体中设置goods表中id列的值,代码:

sql 复制代码
 create or replace trigger tri_insert_good
     before insert   
     on goods        --关于goods数据表,在向其插入新记录之前,引起该触发器的运行
     for each row    --创建行级触发器
 begin
     select seg_id.nextval
     into :new.id
     from dual;      --从序列中生成一个新的数值,赋值给当前插入行的id列

在行级触发器中,可通过"列标识符"访问受操作影响的数据行,分为"原值标识符"和"新值标识符"。原值标识符(如::old.column_name)用于标识列的原始值,常用于UPDATE和DELETE语句,因为INSERT语句中插入的行无原始值;新值标识符(如::new.column_name)用于标识列的新值,常用于INSERT和UPDATE语句,因为DELETE语句中删除的行无法产生新值

sql 复制代码
 --向goods表中插入两条记录,其中一条记录不指定id列的值,由序列seq_id来产生;另一条记录指定id的值
 insert into goods(good_name) values('香蕉');
 insert into goods(id,good_name) values(9,'柚子');
 ​
 commit;
 select * from goods;

无论是否指定id列的值,数据插入都能成功。即使第二次插入时指定id值为9,也无效。因为在触发器中,序列seqid的NEXTVAL属性值被赋给:new.id列标识符,而:new.id就是当前插入行的id列值。NEXTVAL属性值是连续的,不受手动指定值的影响。

4、替换触发器

替换触发器(INSTEAD OF触发器)的"触发时机"关键字是INSTEAD OF,与BEFORE或AFTER不同。它定义在视图上,而非表上。由于视图是多张基表连接的逻辑结构,通常不允许DML操作(INSERT、UPDATE、DELETE)。但为视图编写替换触发器后,对视图的DML操作会变成执行触发器中的PL/SQL块,从而通过触发器代码对基表进行操作。

sql 复制代码
 --创建一个检索员工信息的视图
 create view view_emp_dept
     as select empno,ename,dept.deptno,dname,job,hiredate
         from emp,dept
         where emp.deptno = dept.deptno;
 --尝试向view_emp_dept视图中插入数据,会提示"ORA-01776:无法通过联接视图修改多个基表";
 ​
 --创建一个关于view_emp_dept视图的替换触发器,在该触发器的主体中实现向emp表和dept表中插入两行相互关联的数据
 create or replace trigger tri_insert view
     instead of insert
     on view_emp_dept    --创建一个关于view_emp_dept视图的替换触发器
     for each row        --是行级视图
 declare
     row_dept number;
 begin
     SELECT count(*) INTO row_dept FROM dept WHERE deptno = :new.deptno;--检索指定部门编号的记录行
     if row_dept =0 then     --判断是否存在该部门编号的记录
         insert into dept(deptno,dname)
         values(:new.deptno,:new.dname);     --向dept表中插入数据
     end if;
     insert into emp(empno,ename,deptno,job,hiredate) 
     values(:new.empno,:new.ename,:new.deptno,:new.job,:new.hiredate); --向emp表中插入数据
 end tri_insert_view;
 /

在上述触发器的主体代码中,如果新插入行的部门编号(deptno)不在dept表中,则首先向dept表中插入关于新部门编号的数据行,然后再向emp表中插入记录行,这是因为emp表的外键值(emp.deptno)是dept表的主键值(dept.deptno)。

成功创建触发器ti_insert_view之后,在向view_emp_dept视图中插入数据时,Orace就不会产生错误信息,而是引起触发器ti_inset_view的运行,从而实现向emp表和dept表中插入两行数据

sql 复制代码
 --向视图view_emp_dept中插入一条记录,然后检索插入的记录行
 insert into view_emp_dept (empno,ename,deptno,dname,iob,hiredate) values (7777,'小明',99,'测试部','测试工程师',sysdate)
                                                                           
 --查询deptno=99的数据
 select * from dept where deptno=99;
                                                                           
 --查询empno=7777的数据
 select * from emp where empno=7777;

dept表之前是没有部门编码deptno=99的记录的,触发器中的程序向dept表中插入deptno=99的数据,然后又向emp表中插入一条记录,

5、用户事件触发器

用户事件触发器是因进行DDL操作或用户登录、退出等操作而引起运行的一种触发器,常见引起该类型触发器运行的用户事件包括CREATE、ALTER、DROP、ANALYZE、COMMENT、GRANT、REVOKE、RENAME、TRUNCATE、SUSPEND、LOGON和LOGOFF等。

sql 复制代码
 --使用CREATE TABLE语句创建一个日志信息表,该表保存的日志信息句括数据对象、数据对象类型、操作行为、操作用户和操作日期等
 create table ddl_oper_log
 (
     db_obj_name varchar2(20),   --数据对象名称
     db_obj_type varchar2(20),   --对象类型
     oper_action varchar2(20),   --具体dd1行为
     oper_user varchar2(20),     --操作用户
     oper_date date              --操作日期  
 );

创建一个关于scot用户的DDL操作(这里包括CREATE、ALTER和DROP)的触发器,然后将DDL操作的相关信息插入ddl_oper_log日志表中

sql 复制代码
 create or replace trigger tri_ddl_oper
     before create or alter or drop
     on scott.schema     --在scott模式下,在创建、修改、删除数据对象之前将引发该触发器运行
 begin
     insert into ddl_oper_log values(
         ora_dict_obj_name,          --操作的数据对象名称
         ora_dict_obj_type,          --对象类型
         ora_sysevent,               --系统事件名称
         ora_login_user,             --登录用户
         sysdate);
 end;
 /
 ​

通过上述4个事件属性值和svsdate系统属性就可以将scot用户的DDL操作信息获取出来,然后再把这些信息保存到ddloperlog日志表中。

sql 复制代码
 --首先创建一张数据表和一个视图,然后删除视图和修改数据表,最后使用SELECT语句查看ddl_oper_log日志表中的DDL操作信息
 --创建tb_test表
 create table tb_test(id number);
 --创建view_test视图
 create view view_test as select empno,ename from emp;
 --修改tb_test表
 alter table tb_test add(name varchar2(10));
 --删除视图view_test
 drop view view_test;
 --查询ddl_oper_log表
 select * from ddl_oper_1og;

6、删除触发器

当一个触发器不再使用时,要从内存中删除它

sql 复制代码
 --删除名为tri_dept触发器
 DROP TRIGGER tri_dept;

当一个触发器已经过时,想重新定义时,不必先删除再创建,只需在CREATE语句后面加上OR REPLACE关键字即可

sql 复制代码
 CREATE OR REPLACE TRIGGER my_trigger;

七、程序包

程序包由**PL/SQL程序元素(如变量、类型)和匿名PL/SQL块(如游标)、命名PL/SQL块(如存储过程和函数)**组成,可整体加载到内存中,加快访问速度。例如,PL/SQL中使用DBMS_OUTPUT.PUT_LINE语句,DBMS_OUTPUT是程序包,PUT_LINE是其中的存储过程。程序包通常由规范和包主体组成。

1、程序包的规范

程序包规范规定了在程序包中可以使用的变量、类型、游标和子程序(指各种命名的PL/SQL块)。需要注意的是,程序包规范必须在包主体之前创建。其语法:

sql 复制代码
 CREATE [OR REPLACE ] PACKAGE pack_name IS [declare _variable];   --规范内声明的变量
 [declare _type];            --规范内声明的类型
 [declare _cursor];          --规范内声明的游标
 [declare _function];        --规范内声明的函数
 [declare _procedure];       --规范内声明的存储过程
 END [pack_name];
sql 复制代码
 --创建一个程序包规范,首先在该程序包中声明一个可以获取指定部门的平均工资的函数,然后声明一个可以实现按照指定比例上调指定职务的工资的存储过程,代码如下:
 create or replace package pack_emp is
     function fun_avg_sal(num_deptno number) return number;      --获取指定部门的平均工资
     procedure pro_regulate_sal(var_job varchar2,num_proportion number);--按照指定比例上调指定职务的工资
 end pack emp;
 /

从上述代码中可以看到,在规范中声明的函数和存储过程只有头部的声明,而没有函数体和存储过程主体。

注意:只定义了规范的程序包还不可以被使用,此时如果在PL/SQL块中通过程序包的名称来调用其中的函数或存储过程,则Oracle将会产生错误提示

2、程序包的主体

程序包主体包含规范中声明的游标、过程和函数 的实现代码,还可以声明内部变量 。程序包主体的名称必须与规范名称相同,以便Oracle将二者结合成完整的程序包并编译。实现函数或存储过程主体时,可将每个函数或存储过程作为独立的PL/SQL块处理。与创建规范不同,创建程序包主体使用CREATE PACKAGE BODY语句,而不是CREATE PACKAGE语句。创建程序包主体的代码如下

sql 复制代码
 CREATE [OR REPLACE]PACKAGE BODY pack_name IS    --pack_name程序主体
     [inner_variable]        --程序包主体内部变量
     [cursor_body]           --游标主体
     [function_title]        --从规范中引入的函数头部声明
     {BEGIN
         fun_plsq1;          --PL/SQL语句,函数功能实现
     [EXCEPTION]
         [dowith _ sentences;]   --异常处理语句
     END [fun_name]}         --函数名称
     [procedure_title]       --从规范中引入的存储过程的头部声明
     {BEGIN
         pro_plsql;          --PL/SQL语句,存储过程功能实现
     [EXCEPTION]
         [dowith _ sentences;]
     END [pro_name]}         --存储过程名称
 ...
 END [pack_name];

创建程序包pack_emp的主体,在该主体中实现与规范中声明的函数和存储过程对应:

sql 复制代码
 create or replace package body pack_emp is
     function fun_avg_sal(num_deptno number) return number is
         num_avg_sal number;
     begin
         select avg(sal)
         into num_avg_sal
         from emp
         where deptno = num_deptno;
         return(num_avg_sal);
     exception
         when no_data_found then
             dbms_output.put_line('该部门编号不存在员工记录');
         return 0;
     end fun_avg_sal;
     procedure pro_regulate_sal(var_job varchar2,num_proportion number) is
     begin
         update emp
         set sal = sal*(1+num_proportion)
         where job = var_job;    --为指定的职务调整工资
     end pro_regulate_sal;
 end pack_emp;

创建了程序包的规范和主体后,就可以像普通的存储过程和函数一样实施调用了

sql 复制代码
 --创建一个匿名的PL/SQL块,通过程序包pack_emp调用其中的函数fun_avg_sal和存储过程pro_regulate_sal,并输出函数的返回结果
 set serveroutput on
 ​
 declare
     num_deptno emp.deptno%type;
     var_job emp.job%type;
     num_avg_sal emp.sal%type;
     num_proportion number;
 begin
     num_deptno:=10;
     num_avg_sal:=pack_emp.fun_avg_sal(num_deptno);
     dbms_output.put_line(num_deptno||'号部门的平均工资是:'||num_avg_sal);
     var_job:='SALESMAN';
     num_proportion:=0.1;
     pack_emp.pro_regulate_sal(var_job,num_proportion);
 end;
 /

3、删除程序包

Oracle包的删除可以使用以下SQL语句进行:

sql 复制代码
 DROP PACKAGE [schema.]package_name;

删除包体之前,需要先确保该包不再使用或被其它包体调用。最好的方法是在运行删除操作前,首先查找其它包体是否调用了该包体,并手动删除这些引用。可以使用以下SQL语句查找public synonyms:

sql 复制代码
 SELECT DISTINCT TEXT
 from DBA_DEPENDENCIES
 WHERE TYPE = 'PACKAGE BODY'
 and REFERENCED_NAME = 'Package_name';

假设上述SQL查询没有显示任何结果,则可以执行删除包体操作

学习永无止境,让我们共同进步!!

相关推荐
一屉大大大花卷21 分钟前
初识Neo4j之入门介绍(一)
数据库·neo4j
周胡杰1 小时前
鸿蒙arkts使用关系型数据库,使用DB Browser for SQLite连接和查看数据库数据?使用TaskPool进行频繁数据库操作
前端·数据库·华为·harmonyos·鸿蒙·鸿蒙系统
wkj0011 小时前
navicate如何设置数据库引擎
数据库·mysql
赵渝强老师1 小时前
【赵渝强老师】Oracle RMAN的目录数据库
数据库·oracle
暖暖木头1 小时前
Oracle注释详解
数据库·oracle
御控工业物联网1 小时前
御控网关如何实现MQTT、MODBUS、OPCUA、SQL、HTTP之间协议转换
数据库·sql·http
GJCTYU3 小时前
spring中@Transactional注解和事务的实战理解附代码
数据库·spring boot·后端·spring·oracle·mybatis
MicroTech20253 小时前
微算法科技(NASDAQ: MLGO)探索Grover量子搜索算法,利用量子叠加和干涉原理,实现在无序数据库中快速定位目标信息的效果。
数据库·科技·算法
Code季风3 小时前
SQL关键字快速入门:CASE 实现条件逻辑
javascript·数据库·sql
weixin_478689763 小时前
操作系统【2】【内存管理】【虚拟内存】【参考小林code】
数据库·nosql