存储过程、函数、触发器和包

时间:2021-02-03 05:08:15

一:前言:

存储过程、函数、触发器和包

一:存储过程

存储过程、函数、触发器和包


1:创建储存过程

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 
2 create procedure pro_insertDept is
3
4 begin
5 insert into dept values(77,'市场拓展部','家啊的发放');/*插入记录数据*/
6 commit;
7 dbms_output.put_line('插入新记录成功');
8 end pro_insertDept;
9 /
10
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 create or replace procedure pro_insertDept is
2
3 begin
4 insert into dept values(77,'市场拓展部','家啊的发放');/*插入记录数据*/
5 commit;
6 dbms_output.put_line('插入新记录成功');
7 end pro_insertDept;
8 /
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

从运行结果中可以看出,执行存储过程是成功的;另外,代码中的“execute”命令也可以简写为 “exec”;但有时候需要在一个PL/SQL

程序快中调用某个储存过程




2:存储过程的参数

存储过程、函数、触发器和包


2-1:in 模式参数

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 
2 create or replace procedure insert_dept(
3 num_deptno in number,/*定义in模式的变量,它储存部门编号*/
4 var_ename in varchar2,/*定义in模式的变量,它储存部门名称*/
5 var_loc in varchar2
6 ) is
7 begin
8 insert into dept values(num_deptno,var_ename,var_loc); /*向dept表中插入记录*/
9 commit; /*提交数据库*/
10 end insert_dept;
11 /
12
13
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 begin
2 insert_dept(var_ename=>'asadad',var_loc=>'asdfasd',num_deptno=>15) ;
3 end;
4 /
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 begin
2 insert_dept(28,'asdfa','fasdf');
3 end;
4 /
5
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 SQL> exec insert_dept(38,var_loc=>'adfasd',var_ename=>'adfasd');
2
3 PL/SQL procedure successfully completed
View Code

存储过程、函数、触发器和包



2-2: out 模式参数

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 create or replace procedure select_dept(
2 num_deptno in number,/*定义 in 模式变量 要求输入部门编号*/
3 var_dname out dept.dname%type,/*定义out模式变量,可以储存部门名称并输出*/
4 var_loc out dept.loc%type
5 )
6 is
7
8 begin
9 select dname,loc into var_dname,var_loc from dept where deptno=num_deptno;
10 exception
11 when no_data_found then
12 dbms_output.put_line('该部门编号不存在');
13
14 end select_dept;
15 /
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 set serveroutput on
2 declare
3 var_dname dept.dname%type;/*声明变量,对应过程中的out模式的var_dname */
4 var_loc dept.loc%type;/*声明变量,对应过程这哦功能的out模式的var_loc*/
5 begin
6 select_dept(99,var_dname ,var_loc );/*传入部门编号然后输出部门名称和位置信息*/
7 dbms_output.put_line(var_dname||'位于:'||var_loc); /*输出部门信息*/
8 end;
9 /
View Code

存储过程、函数、触发器和包

在上面代码中,把声明的两个变量传入熬存储过程中,当存储过程执行时,其中的out参数会被赋值,当存储过程执行完毕,out参数的值会在调用处返回,这样定义的两个变量就可以得到out参数被赋予的值,最后这两个值就可以在储存过程外任意使用了。

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 variable var_dname varchar2(50);
2 variable var_loc varchar2(50);
3 exec select_dept(15,:var_dname,:var_loc );
4
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包


2-3:in  out 模式参数

存储过程、函数、触发器和包

  1 create or replace procedure pro_square(  2  num in out number ,/*计算它的平方或者是平方根,这是一个‘int  out ’参数*/
3 flag in boolean
4 )is
5 i int :=2;
6 begin
7 if flag then /*true*/
8 num:=power(num,i);
9 else
10 num :=sqrt(num);
11
12 end if;
13
14 end pro_square;
15 /

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 declare  3   var_number number;/*储存要进行运算的值和运算后的结果*/  4   var_temp number;/*储存要进行运算的值*/  5   boo_flag boolean;/*平方或者 平方根的逻辑标记*/  6 begin  7   var_temp:=3;/*变量赋值*/  8   var_number:=var_temp;  9   boo_flag:=false;/*false 表示计算平方根 true 表示计算平方*/ 10   pro_square(var_number,boo_flag); 11   if boo_flag then 12      dbms_output.put_line(var_temp ||'的平方是:'||var_number);/*输出计算结果*/ 13   else 14       dbms_output.put_line(var_temp ||'的平方根是:'||var_number);/*输出计算结果*/ 15    end if; 16 end; 17 / 18 

存储过程、函数、触发器和包

存储过程、函数、触发器和包




2-4:in 参数的默认值

存储过程、函数、触发器和包

  1 create or replace procedure insert_dept1(  2   num_deptno in number,/*定义储存部门编号的in参数*/  3   var_dname in varchar2 default '综合部',/*定义储存部门名称的in 参数 并初始化默认值*/  4   var_loc in varchar2 default '北京'  5 ) is  6   7 begin  8    insert into dept values (num_deptno,var_dname,var_loc) ;  9  10 end insert_dept1; 11 / 12 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1 declare  2    row_dept dept%rowtype ;/*定义行变量,与dept表的一行类型相同*/  3 begin  4    insert_dept1(57,var_loc => '太原');/*调用 insert_dept存储过程,插入参数*/  5    commit;  6     select *  into row_dept from dept where deptno =57 ;/*查询新插入的记录*/  7     dbms_output.put_line('部门名称是:'||row_dept.dname||',位置是:'||row_dept.loc);  8 end;  9 /

存储过程、函数、触发器和包

存储过程、函数、触发器和包



3:存储过程示例:

 


3-1:中规中矩的 loop  while 循环。常态是拼接游标的方式循环数据

存储过程、函数、触发器和包

  1 create or replace procedure proc_yszxtz_aduit (  2   yszxtz_ids in varchar2  /* 预算执行调整表id,其值为: '1,2,3,4,5,6,7,8'  */  3 ) is  4   5 rowvar_yszxtz ys_zxtz%rowtype    ; /*声明了:预算执行调整主表 单条数据对象 */  6 ---  预算执行调整主表的id的 游标   7 var_sql varchar2(4000);  8 cursor yszxtz_cursor is   select   *   into  rowvar_yszxtz from ys_zxtz z where  1=1       ;  9 begin 10     dbms_output.put_line('----===========预算执行调整审批:审结后业务处理开始-----====== '); 11 /*进行:入参*/ 12 if yszxtz_ids is   null then 13      dbms_output.put_line(' 预算执行调整主表的id值不允许 null');     /*则输出异常提示信息*/ 14      rollback; /*回滚插入的数据记录*/ 15 end if; 16  17 --循环开始 18  19   /*判断 split_cursor 是否已经打开*/ 20   if not  yszxtz_cursor%isopen  then 21      open yszxtz_cursor   ;  /*打开游标 */ 22   end if; 23   fetch yszxtz_cursor   into  rowvar_yszxtz;/*先让指针指向结果集中的第一行,并将值保存到emp_row 中*/ 24   while yszxtz_cursor%found loop 25     dbms_output.put_line('预算执行调整的id:' || rowvar_yszxtz.id  ); 26  27  28  29   fetch yszxtz_cursor    into  rowvar_yszxtz;/*先让指针指向结果集中的第一行,并将值保存到emp_row 中*/ 30    /*退出循环的条件*/ 31   exit when yszxtz_cursor%notfound or yszxtz_cursor%notfound is null; 32  33    dbms_output.put_line('---------------------------------------------预算执行调整的 业务逻辑处理结束---------------------------------------------' ); 34   end loop; 35 close yszxtz_cursor;/*关闭游标*/ 36 dbms_output.put_line('---------------------------------------------预算执行调整的 业务逻辑处理结束:关闭游标---------------------------------------------' ); 37  38 end proc_yszxtz_aduit; 39 


3-2:动态拼接 游标的sql语句和参数条件的方式 进行 loop循环

存储过程、函数、触发器和包

  1 create or replace procedure proc_yszxtz_aduit_1 (  2   yszxtz_ids in varchar2  /* 预算执行调整表id,其值为: 1,2,3,4,5,6,7,8 */  3 )  4 is  5 type ref_cursor_type is ref cursor;  --定义一个动态游标  6 yszxtz_cursor ref_cursor_type;  7 rowvar_yszxtz ys_zxtz%rowtype    ; /*声明:预算执行调整主表 单条数据对象 */  8 rowvar_adjustMx  view_yszxtzms_adjustmx%rowtype;/*声明:view_yszxtzms_adjustmx 视图 行数据对象*/  9 var_gkadjustid number(20);/*声明:获取 gk_adjust 表主键 对象*/ 10  11 begin 12     dbms_output.put_line('============================================预算执行调整审批:审结后业务处理开始============================================ '); 13 /*进行:入参*/ 14 if yszxtz_ids is   null then 15      dbms_output.put_line(' 预算执行调整主表的id值不允许 null');     /*则输出异常提示信息*/ 16      rollback; /*回滚插入的数据记录*/ 17 end if; 18  19 --循环开始 20    --打开游标      21   open yszxtz_cursor for 'select  *  from ys_zxtz z where  1=1 and  z.id in ('||   yszxtz_ids ||' )' ; 22     dbms_output.put_line('============================================ 预算执行调整的 业务逻辑处理 开始:执行的sql为: '|| 'select  *  from ys_zxtz z where  1=1 and  z.id in ('||   yszxtz_ids ||' )============================================'  );     /*则输出异常提示信息*/ 23   fetch yszxtz_cursor   into  rowvar_yszxtz;/*先让指针指向结果集中的第一行,并将值保存到emp_row 中*/ 24   while yszxtz_cursor%found loop 25     var_gkadjustid := nextid('GK_ADJUST_SEQ');/*获取预算执行调整主表的id值*/ 26    dbms_output.put_line('============================================预算执行调整的 业务逻辑处理 开始:    其id:' || rowvar_yszxtz.id ||';gk_adjust 表主键值:var_gkadjustid='||var_gkadjustid||'===========================================' ); 27  28  29   fetch yszxtz_cursor    into  rowvar_yszxtz;/*先让指针指向结果集中的第一行,并将值保存到emp_row 中*/ 30    /*退出循环的条件*/ 31   exit when yszxtz_cursor%notfound or yszxtz_cursor%notfound is null; 32    dbms_output.put_line('============================================预算执行调整的 业务逻辑处理     结束:其id:' || rowvar_yszxtz.id ||'=====================================================================================' ); 33   end loop; 34 close yszxtz_cursor;/*关闭游标*/ 35 dbms_output.put_line('============================================预算执行调整的 业务逻辑处理结束:关闭游标============================================' ); 36  37 end proc_yszxtz_aduit_1; 38 


3-3:使用包头、包体的形式

  1 --包头   2 create or replace package mypackage as  3  type empcursor is ref cursor; --声明一个光标类型  4  procedure queryEmpList(dno in number,empList out empcursor);  5 end;  6   7 --创建包体   8 create or replace package body mypackage as  9  procedure queryEmpList(dno in number,empList out empcursor) as 10    begin 11      --打开光标 12      open empList for select * from emp where deptno=dno; 13    end; 14 end; 15 


3-4: 使用存储过程,返回游标的形式

  1 --定义一个返回程序集的引用游标   2 CREATE OR REPLACE PACKAGE BAF_QUERY_TABLE AS  3   TYPE P_CURSOR IS ref CURSOR;  4 END BAF_QUERY_TABLE;  5   6 --创建存储过程,并返回游标的形式返回程序集   7 create or replace procedure getList(p_eno number, p_out_cursor out BAF_QUERY_TABLE.P_CURSOR) is  8 begin  9   --没有给定员工ID则返回所有员工信息 10   if p_eno is null then 11       open p_out_cursor for select * from emp; 12   else 13     --返回指定ID的员工信息 14       open p_out_cursor for select * from emp where empno = p_eno; 15   end if; 16  17 end getList; 18  19  20 --以上创建的包还可以给存储函数使用 21 create or replace function sp_ListEmp return BAF_QUERY_TABLE.P_CURSOR 22 as 23     l_cursor    BAF_QUERY_TABLE.P_CURSOR; 24 begin 25     open l_cursor for select ename, empno from emp order by ename; 26     return l_cursor; 27 end;


3-5:使用sys_refcursor类型

  1 create or replace procedure rsCursor(p_eno number,rs_cursor out SYS_REFCURSOR)  2 AS  3 BEGIN  4  --没有给定员工ID则返回所有员工信息  5   if p_eno is null then  6       OPEN rs_cursor for select * from emp;  7   else  8     --返回指定ID的员工信息  9       OPEN rs_cursor for select * from emp where deptno = p_eno ; 10   end if; 11  12 END;


java中调用

  1 Connection conn = null;  2         //sql语句 (一定要写上包名)  3         String sql = "{call mypackage.queryEmpList(?,?)}";  4   5         try {  6             //获取数据库的连接  7             conn = JDBCUtil.getConnection();  8             //通过连接创建statment  9             CallableStatement call = conn.prepareCall(sql); 10  11             //对于IN参数需要赋值 12             call.setInt(1,10); 13  14             //对于OUT参数需要先申明 15             call.registerOutParameter(2,OracleTypes.CURSOR); 16  17             //执行调用 18             call.execute(); 19  20             //取出该部门中所有员工信息(注意这里) 21             ResultSet rs = ((OracleCallableStatement)call).getCursor(2); 22  23             while(rs.next()){ 24                 //可以取出sql语句中查询的所有字段(这里只取几个演示下) 25                 int empno = rs.getInt("empno"); 26                 String ename = rs.getString("ename"); 27                 double sal = rs.getDouble("sal"); 28                 System.out.println("=================================================="); 29                 System.out.println("empno:"+empno+"\t ename:"+ename+"\t sal:"+sal); 30                 System.out.println("=================================================="); 31             }


3-6:存储过程的常用技巧

注意:本段内容来源:《oracle 存储过程,游标、异常处理,集合使用入门详解

在进行pl/sql编程时打交道最多的就是存储过程了。存储过程的结构是非常的简单的,我们在这里除了学习存储过程的基本结构外,还会学习编写存储过程时相关的一些实用的知识。如:游标的处理,异常的处理,集合的选择等等

1.存储过程结构


1.1 第一个存储过程

  1 create or replace procedure proc1(  2   p_para1 varchar2,  3   p_para2 out varchar2,  4   p_para3 in out varchar2  5 )as  6  v_name varchar2(20);  7 begin  8   v_name := '张三丰';  9   p_para3 := v_name; 10   dbms_output.put_line('p_para3:'||p_para3); 11 end;

上面就是一个最简单的存储过程。一个存储过程大体分为这么几个部分:
创建语句:create or replace procedure 存储过程名
如果没有or replace语句,则仅仅是新建一个存储过程。如果系统存在该存储过程,则会报错。Create or replace procedure 如果系统中没有此存储过程就新建一个,如果系统中有此存储过程则把原来删除掉,重新创建一个存储过程。
存储过程名定义:包括存储过程名和参数列表。参数名和参数类型。参数名不能重复, 参数传递方式:IN, OUT, IN OUT
IN 表示输入参数,按值传递方式。
OUT 表示输出参数,可以理解为按引用传递方式。可以作为存储过程的输出结果,供外部调用者使用。
IN OUT 即可作输入参数,也可作输出参数。
参数的数据类型只需要指明类型名即可,不需要指定宽度。
参数的宽度由外部调用者决定。
过程可以有参数,也可以没有参数
变量声明块:紧跟着的as (is )关键字,可以理解为pl/sql的declare关键字,用于声明变量。
变量声明块用于声明该存储过程需要用到的变量,它的作用域为该存储过程。另外这里声明的变量必须指定宽度。遵循PL/SQL的变量声明规范。
过程语句块:从begin 关键字开始为过程的语句块。存储过程的具体逻辑在这里来实现。
异常处理块:关键字为exception ,为处理语句产生的异常。该部分为可选
结束块:由end关键字结果。

1.2 存储过程的参数传递方式


存储过程的参数传递有三种方式:IN,OUT,IN OUT .
IN 按值传递,并且它不允许在存储过程中被重新赋值。如果存储过程的参数没有指定存参数传递类型,默认为IN

  1 create or replace procedure proc1(  2   p_para1 varchar2,  3   p_para2 out varchar2,  4   p_para3 in out varchar2  5 )as  6  v_name varchar2(20);  7 begin  8   p_para1 :='aaa';  9   p_para2 :='bbb'; 10   v_name := '张三丰'; 11   p_para3 := v_name; 12   dbms_output.put_line('p_para3:'||p_para3); 13   null; 14 end; 15  16 Warning: Procedure created with compilation errors 17  18 SQL> show error; 19 Errors for PROCEDURE LIFEMAN.PROC1: 20  21 LINE/COL ERROR 22 -------- ----------------------------------------------------------------------    23 8/3      PLS-00363: expression 'P_PARA1' cannot be used as an assignment target 24 8/3      PL/SQL: Statement ignored

这一点与其它高级语言都不同。它相当于java在参数前面加上final关键字。
OUT 参数:作为输出参数,需要注意,当一个参数被指定为OUT类型时,就算在调用存储过程之前对该参数进行了赋值,在存储过程中该参数的值仍然是null.

  1 create or replace procedure proc1(  2   p_para1 varchar2,  3   p_para2 out varchar2,  4   p_para3 in out varchar2  5 )as  6  v_name varchar2(20);  7 begin  8   v_name := '张三丰';  9   p_para3 := v_name; 10   dbms_output.put_line('p_para1:'||p_para1); 11   dbms_output.put_line('p_para2:'||p_para2); 12   dbms_output.put_line('p_para3:'||p_para3); 13 end; 14  15 SQL> var p1 varchar2(10); 16 SQL> var p2 varchar2(10); 17 SQL> var p3 varchar2(10); 18 SQL> exec :p1 :='aaaa'; 19 SQL> exec :p2 :='bbbb'; 20 SQL> exec :p3 :='cccc'; 21 SQL> exec proc1(:p1,:p2,:p3); 22 p_para1:aaaa 23 p_para2: 24 p_para3:张三丰 25 SQL> exec dbms_output.put_line(:p2); 26  27  28 PL/SQL procedure successfully completed 29 p2 30 ---------   31 

INOUT 是真正的按引用传递参数。即可作为传入参数也可以作为传出参数。

  1 1.3 存储过程参数宽度  2 create or replace procedure proc1(  3   p_para1 varchar2,  4   p_para2 out varchar2,  5   p_para3 in out varchar2  6 )as  7  v_name varchar2(2);  8 begin  9   v_name := p_para1; 10 end; 11  12 SQL> var p1 varchar2(10); 13 SQL> var p2 varchar2(20); 14 SQL> var p3 varchar2(30); 15 SQL> exec :p1 :='aaaaaa'; 16 SQL> exec proc1(:p1,:p2,:p3); 17  18  19 ORA-06502: PL/SQL: numeric or value error: character string buffer too small 20 ORA-06512: at "LIFEMAN.PROC1", line 8 21 ORA-06512: at line 1

首先,我们要明白,我们无法在存储过程的定义中指定存储参数的宽度,也就导致了我们无法在存储过程中控制传入变量的宽度。这个宽度是完全由外部传入时决定的。
我们再来看看OUT类型的参数的宽度

  1 create or replace procedure proc1(  2   p_para1 varchar2,  3   p_para2 out varchar2,  4   p_para3 in out varchar2  5 )as  6  v_name varchar2(2);  7 begin  8   p_para2 :='aaaaaaaaaaaaaaaaaaaa';  9 end; 10 SQL> var p1 varchar2(1); 11 SQL> var p2 varchar2(1); 12 SQL> var p3 varchar2(1); 13 SQL> exec :p2 :='a'; 14 SQL> exec proc1(:p1,:p2,:p3);


在该过程中,p_para2被赋予了20个字符a.
而在外部的调用过程中,p2这个参数仅仅被定义为varchar2(1).
而把p2作为参数调用这个过程,却并没有报错。而且它的真实值就是20个a

  1 SQL> select dump(:p2) from dual;  2 DUMP(:P2)  3 ---------------------------------------------------------------------------     4 Typ=1 Len=20: 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97  5 p2  6 ---------     7 aaaaaaaaaaaaaaaaaaaa  8   9     再来看看IN OUT参数的宽度 10 create or replace procedure proc1( 11   p_para1 varchar2, 12   p_para2 out varchar2, 13   p_para3 in out varchar2 14 )as 15  v_name varchar2(2); 16 begin 17   p_para3 :='aaaaaaaaaaaaaaaaaaaa'; 18 end; 19  20 SQL> var p1 varchar2(1); 21 SQL> var p2 varchar2(1); 22 SQL> var p3 varchar2(1); 23 SQL> exec proc1(:p1,:p2,:p3);


执行这个过程,仍然正确执行。
可见,对于IN参数,其宽度是由外部决定。
对于OUT 和IN OUT 参数,其宽度是由存储过程内部决定。
因此,在写存储过程时,对参数的宽度进行说明是非常有必要的,最明智的方法就是参数的数据类型使用%type。这样双方就达成了一致。

1.3 参数的默认值


存储过程的参数可以设置默认值

  1 create or replace procedure procdefault(p1 varchar2,  2                                         p2 varchar2 default 'mark')  3 as  4 begin  5   dbms_output.put_line(p2);  6 end;  7   8 SQL> set serveroutput on;  9 SQL> exec procdefault('a');

mark
可以通过default 关键字为存储过程的参数指定默认值。在对存储过程调用时,就可以省略默认值。
需要注意的是:默认值仅仅支持IN传输类型的参数。OUT 和 IN OUT不能指定默认值
对于有默认值的参数不是排在最后的情况。

  1 create or replace procedure procdefault2(p1 varchar2 default 'remark',  2                                         p2 varchar2 )  3 as  4 begin  5   dbms_output.put_line(p1);  6 end;

第一个参数有默认值,第二个参数没有。如果我们想使用第一个参数的默认值时
exec procdefault2('aa');
这样是会报错的。
那怎么变呢?可以指定参数的值。

  1 SQL> exec procdefault2(p2 =>'aa');


emark
这样就OK了,指定aa传给参数p2

2. 存储过程内部块


2.1 内部块


我们知道了存储过程的结构,语句块由begin开始,以end结束。这些块是可以嵌套。在语句块中可以嵌套任何以下的块。

  1 Declarebeginexceptionend;  2 create or replace procedure innerBlock(p1 varchar2)  3 as  4   o1 varchar2(10) := 'out1';  5 begin  6   dbms_output.put_line(o1);  7   declare  8     inner1 varchar2(20);  9   begin 10     inner1 :='inner1'; 11     dbms_output.put_line(inner1); 12  13     declare 14       inner2 varchar2(20); 15     begin 16       inner2 := 'inner2'; 17       dbms_output.put_line(inner2); 18     end; 19   exception 20     when others then 21       null; 22   end; 23 end;


需要注意变量的作用域。

3.存储过程的常用技巧


3.1 哪种集合?


我们在使用存储过程的时候经常需要处理记录集,也就是多条数据记录。分为单列多行和多列多行,这些类型都可以称为集合类型。我们在这里进行比较这些集合类型,以便于在编程时做出正确的选择。
索引表,也称为pl/sql表,不能存储于数据库中,元素的个数没有限制,下标可以为负值。

  1 type t_table is table of varchar2(20) index by binary_integer;  2  v_student t_table;

varchar2(20)表示存放元素的数据类型,binary_integer表示元素下标的数据类型。
嵌套表,索引表没有 index by子句就是嵌套表,它可以存放于数据中,元素个数无限,下标从1开始,并且需要初始化

  1 type t_nestTable is table of varchar2(20);  2 v_class t_nestTable ;


仅是这样声明是不能使用的,必须对嵌套表进行初始化,对嵌套表进行初始化可以使用它的构造函数

  1 v_class :=t_nestTable('a','b','c');

  1 type t_array is varray (20) of varchar2(20);
长数组,变长数组与高级语言的数组类型非常相似,下标以1开始,元素个数有限。
  1 type t_array is varray (20) of varchar2(20);


varray(20)就定义了变长数组的最大元素个数是20个
变长数组与嵌套表一样,也可以是数据表列的数据类型。
同时,变长数组的使用也需要事先初始化。
类型 可存储于数据库 元素个数 是否需初始化 初始下标值
索引表 否 无限 不需
嵌套表 可 无限 需 1
可变数组 可 有限(自定义) 需 1
由此可见,如果仅仅是在存储过程中当作集合变量使用,索引表是最好的选择。

3.2 选用何种游标?


显示游标分为:普通游标,参数化游标和游标变量三种。
下面以一个过程来进行说明

  1 create or replace procedure proccursor(p varchar2)  2 as  3 v_rownum number(10) := 1;  4 cursor c_postype is select pos_type from pos_type_tbl where rownum =1;  5 cursor c_postype1 is select pos_type from pos_type_tbl where rownum = v_rownum;  6 cursor c_postype2(p_rownum number) is select pos_type from pos_type_tbl where rownum = p_rownum;  7 type t_postype is ref cursor ;  8 c_postype3 t_postype;  9 v_postype varchar2(20); 10 begin 11   open c_postype; 12   fetch c_postype into v_postype; 13   dbms_output.put_line(v_postype); 14   close c_postype; 15   open c_postype1; 16   fetch c_postype1 into v_postype; 17   dbms_output.put_line(v_postype); 18   close c_postype1; 19   open c_postype2(1); 20   fetch c_postype2 into v_postype; 21   dbms_output.put_line(v_postype); 22   close c_postype2; 23   open c_postype3 for select pos_type from pos_type_tbl where rownum =1; 24   fetch c_postype3 into v_postype; 25   dbms_output.put_line(v_postype); 26   close c_postype3; 27 end; 28 


cursor c_postype is select pos_type from pos_type_tbl where rownum =1
这一句是定义了一个最普通的游标,把整个查询已经写死,调用时不可以作任何改变。
cursor c_postype1 is select pos_type from pos_type_tbl where rownum = v_rownum;
这一句并没有写死,查询参数由变量v_rownum来决定。需要注意的是v_rownum必须在这个游标定义之前声明。
cursor c_postype2(p_rownum number) is select pos_type from pos_type_tbl where rownum = p_rownum;
这一条语句与第二条作用相似,都是可以为游标实现动态的查询。但是它进一步的缩小了参数的作用域范围。但是可读性降低了不少。
type t_postype is ref cursor ;
c_postype3 t_postype;
先定义了一个引用游标类型,然后再声明了一个游标变量。
open c_postype3 for select pos_type from pos_type_tbl where rownum =1;
然后再用open for 来打开一个查询。需要注意的是它可以多次使用,用来打开不同的查询。
从动态性来说,游标变量是最好用的,但是阅读性也是最差的。
注意,游标的定义只能用使关键字IS,它与AS不通用。

3.3 游标循环最佳策略

我们在进行PL/SQL编程时,经常需要循环读取结果集的数据。进行逐行处理,这个过程就需要对游标进行循环。对游标进行循环的方法有多种,我们在此一一分析。

  1 create or replace procedure proccycle(p varchar2)  2 as  3 cursor c_postype is select pos_type, description from pos_type_tbl where rownum < 6;  4 v_postype varchar2(20);  5 v_description varchar2(50);  6 begin  7 open c_postype;  8   if c_postype%found then  9     dbms_output.put_line('found true'); 10   elsif c_postype%found = false then 11     dbms_output.put_line('found false'); 12   else 13     dbms_output.put_line('found null'); 14   end if; 15   loop 16    fetch c_postype into v_postype,v_description ; 17    exit when c_postype%notfound; 18    dbms_output.put_line('postype:'||v_postype||',description:'||v_description); 19   end loop; 20   close c_postype; 21 dbms_output.put_line('---loop end---'); 22   open c_postype; 23     fetch c_postype into v_postype,v_description; 24     while c_postype%found loop 25       dbms_output.put_line('postype:'||v_postype||',description:'||v_description); 26       fetch c_postype into v_postype,v_description ; 27     end loop; 28  29   close c_postype; 30 dbms_output.put_line('---while end---'); 31   for v_pos in c_postype loop 32     v_postype := v_pos.pos_type; 33     v_description := v_pos.description; 34     dbms_output.put_line('postype:'||v_postype||',description:'||v_description); 35   end loop; 36   dbms_output.put_line('---for end---'); 37 end;


使用游标之前需要开打游标,open cursor,循环完后再关闭游标close cursor.
这是使用游标应该慎记于心的法则。
上面的过程演示了游标循环的三种方法。
在讨论循环方法之前,我们先看看%found和%notfound这些游标的属性。

  1 open c_postype;  2  if c_postype%found then  3    dbms_output.put_line('found true');  4  elsif c_postype%found = false then  5    dbms_output.put_line('found false');  6  else  7    dbms_output.put_line('found null');  8  end if;

在打开一个游标之后,马上检查它的%found或%notfound属性,它得到的结果即不是true也不是false.而是null.必须执行一条fetch语句后,这些属性才有值。

第一种使用loop 循环 

  1 loop  2    fetch c_postype into v_postype,v_description ;  3    exit when c_postype%notfound;  4    ……  5 end loop


这里需要注意,exit when语句一定要紧跟在fetch之后。必避免多余的数据处理。
处理逻辑需要跟在exit when之后。这一点需要多加小心。
循环结束后要记得关闭游标。

第二种使用while循环


  1 fetch c_postype into v_postype,v_description;  2 while c_postype%found loop  3    ……  4       fetch c_postype into v_postype,v_description ;  5 end loop;

我们知道了一个游标打开后,必须执行一次fetch语句,游标的属性才会起作用。所以使用while 循环时,就需要在循环之前进行一次fetch动作。
而且数据处理动作必须放在循环体内的fetch方法之前。循环体内的fetch方法要放在最后。否则就会多处理一次。这一点也要非常的小心。
总之,使用while来循环处理游标是最复杂的方法。

第三种 for循环 


  1 for v_pos in c_postype loop  2    v_postype := v_pos.pos_type;  3    v_description := v_pos.description;  4  5  end loop;
.

可见for循环是比较简单实用的方法。
首先,它会自动open和close游标。解决了你忘记打开或关闭游标的烦恼。
其它,自动定义了一个记录类型及声明该类型的变量,并自动fetch数据到这个变量中。
我们需要注意v_pos 这个变量无需要在循环外进行声明,无需要为其指定数据类型。
它应该是一个记录类型,具体的结构是由游标决定的。
这个变量的作用域仅仅是在循环体内。
把v_pos看作一个记录变量就可以了,如果要获得某一个值就像调用记录一样就可以了。
如v_pos.pos_type
由此可见,for循环是用来循环游标的最好方法。高效,简洁,安全。
但遗憾的是,常常见到的却是第一种方法。所以从今之后得改变这个习惯了。

3.4 select into不可乎视的问题


我们知道在pl/sql中要想从数据表中向变量赋值,需要使用select into 子句。
但是它会带动来一些问题,如果查询没有记录时,会抛出no_data_found异常。
如果有多条记录时,会抛出too_many_rows异常。
这个是比较糟糕的。一旦抛出了异常,就会让过程中断。特别是no_data_found这种异常,没有严重到要让程序中断的地步,可以完全交给由程序进行处理。

  1 create or replace procedure procexception(p varchar2)  2 as  3   v_postype varchar2(20);  4 begin  5    select pos_type into v_postype from pos_type_tbl where 1=0;  6     dbms_output.put_line(v_postype);  7 end;

执行这个过程

  1 SQL> exec procexception('a');  2 报错  3 ORA-01403: no data found  4 ORA-06512: at "LIFEMAN.PROCEXCEPTION", line 6  5 ORA-06512: at line 1  6 

处理这个有三个办法
1. 直接加上异常处理。

  1 create or replace procedure procexception(p varchar2)  2 as  3   v_postype varchar2(20);  4   5 begin  6    select pos_type into v_postype from pos_type_tbl where 1=0;  7     dbms_output.put_line(v_postype);  8 exception  9   when no_data_found then 10     dbms_output.put_line('没找到数据'); 11 end;

这样做换汤不换药,程序仍然被中断。可能这样不是我们所想要的。
2. select into做为一个独立的块,在这个块中进行异常处理

  1 create or replace procedure procexception(p varchar2)  2 as  3   v_postype varchar2(20);  4   5 begin  6   begin  7    select pos_type into v_postype from pos_type_tbl where 1=0;  8     dbms_output.put_line(v_postype);  9  exception 10   when no_data_found then 11     v_postype := ''; 12   end; 13   dbms_output.put_line(v_postype); 14 end;

这是一种比较好的处理方式了。不会因为这个异常而引起程序中断。
3.使用游标

  1 create or replace procedure procexception(p varchar2)  2 as  3   v_postype varchar2(20);  4   cursor c_postype is select pos_type  from pos_type_tbl where 1=0;  5 begin  6   open c_postype;  7     fetch c_postype into v_postype;  8   close c_postype;  9   dbms_output.put_line(v_postype); 10 end;


这样就完全的避免了no_data_found异常。完全交由程序员来进行控制了。
第二种情况是too_many_rows 异常的问题。
Too_many_rows 这个问题比起no_data_found要复杂一些。
给一个变量赋值时,但是查询结果有多个记录。
处理这种问题也有两种情况:
1. 多条数据是可以接受的,也就是说从结果集中随便取一个值就行。这种情况应该很极端了吧,如果出现这种情况,也说明了程序的严谨性存在问题。
2. 多条数据是不可以被接受的,在这种情况肯定是程序的逻辑出了问题,也说是说原来根本就不会想到它会产生多条记录。
对于第一种情况,就必须采用游标来处理,而对于第二种情况就必须使用内部块来处理,重新抛出异常。
多条数据可以接受,随便取一条,这个跟no_data_found的处理方式一样,使用游标。
我这里仅说第二种情况,不可接受多条数据,但是不要忘了处理no_data_found哦。这就不能使用游标了,必须使用内部块。

  1 create or replace procedure procexception2(p varchar2)  2 as  3   v_postype varchar2(20);  4   5 begin  6   begin  7     select pos_type into v_postype from pos_type_tbl where rownum < 5;  8   exception  9     when no_data_found then 10       v_postype :=null; 11     when too_many_rows then 12       raise_application_error(-20000,'对v_postype赋值时,找到多条数据'); 13   end; 14  dbms_output.put_line(v_postype); 15 end;


需要注意的是一定要加上对no_data_found的处理,对出现多条记录的情况则继续抛出异常,让上一层来处理。
总之对于select into的语句需要注意这两种情况了。需要妥当处理啊。
3.5 在存储过程中返回结果集
我们使用存储过程都是返回值都是单一的,有时我们需要从过程中返回一个集合。即多条数据。这有几种解决方案。比较简单的做法是写临时表,但是这种做法不灵活。而且维护麻烦。我们可以使用嵌套表来实现.没有一个集合类型能够与java的jdbc类型匹配。这就是对象与关系数据库的阻抗吧。数据库的对象并不能够完全转换为编程语言的对象,还必须使用关系数据库的处理方式。


  1 create or replace package procpkg is  2    type refcursor is ref cursor;  3    procedure procrefcursor(p varchar2, p_ref_postypeList  out refcursor);  4 end procpkg;  5   6 create or replace package body procpkg is  7   procedure procrefcursor(p varchar2, p_ref_postypeList out  refcursor)  8   is  9     v_posTypeList PosTypeTable; 10   begin 11     v_posTypeList :=PosTypeTable();--初始化嵌套表    12     v_posTypeList.extend; 13     v_posTypeList(1) := PosType('A001','客户资料变更'); 14     v_posTypeList.extend; 15     v_posTypeList(2) := PosType('A002','团体资料变更'); 16     v_posTypeList.extend; 17     v_posTypeList(3) := PosType('A003','受益人变更'); 18     v_posTypeList.extend; 19     v_posTypeList(4) := PosType('A004','续期交费方式变更'); 20     open p_ref_postypeList for  select * from table(cast (v_posTypeList as PosTypeTable)); 21   end; 22 end procpkg;


在包头中定义了一个游标变量,并把它作为存储过程的参数类型。
在存储过程中定义了一个嵌套表变量,对数据写进嵌套表中,然后把嵌套表进行类型转换为table,游标变量从这个嵌套表中进行查询。外部程序调用这个游标。
所以这个过程需要定义两个类型。

  1 create or replace type PosType as Object (  2   posType varchar2(20),  3   description varchar2(50)  4 );

create or replace type PosTypeTable is table of PosType;
需要注意,这两个类型不能定义在包头中,必须单独定义,这样java层才能使用。
在外部通过pl/sql来调用这个过程非常简单。

  1 set serveroutput on;  2 declare  3   type refcursor is ref cursor;  4   v_ref_postype refcursor;  5   v_postype varchar2(20);  6   v_desc varchar2(50);  7 begin  8   procpkg.procrefcursor('a',v_ref_postype);  9   loop 10     fetch  v_ref_postype into v_postype,v_desc; 11     exit when v_ref_postype%notfound; 12     dbms_output.put_line('posType:'|| v_postype || ';description:' || v_desc); 13   end loop; 14 end;


注意:对于游标变量,不能使用for循环来处理。因为for循环会隐式的执行open动作。而通过open for来打开的游标%isopen是为true的。也就是默认打开的。Open一个已经open的游标是错误的。所以不能使用for循环来处理游标变量。
我们主要讨论的是如何通过jdbc调用来处理这个输出参数。

  1 conn = this.getDataSource().getConnection();  2 CallableStatement call = conn.prepareCall("{call procpkg.procrefcursor(?,?)}");  3 call.setString(1, null);  4 call.registerOutParameter(2, OracleTypes.CURSOR);  5 call.execute();  6 ResultSet rsResult = (ResultSet) call.getObject(2);  7 while (rsResult.next()) {  8   String posType = rsResult.getString("posType");  9   String description = rsResult.getString("description"); 10   ...... 11 }


这就是jdbc的处理方法。
Ibatis处理方法:
1.参数配置


  1 <parameterMap id="PosTypeMAP" class="java.util.Map">  2  <parameter property="p" jdbcType="VARCHAR" javaType="java.lang.String" />  3  <parameter property="p_ref_postypeList" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" mode="OUT" typeHandler="com.palic.elis.pos.dayprocset.integration.dao.impl.CursorHandlerCallBack" />  4 </parameterMap>  5   6 2.调用过程  7   <procedure id ="procrefcursor" parameterMap ="PosTypeMAP">  8       {call procpkg.procrefcursor(?,?)}  9   </procedure> 10  11 3.定义自己的处理器 12   public class CursorHandlerCallBack implements TypeHandler{ 13     public Object getResult(CallableStatement cs, int index) throws SQLException { 14         ResultSet rs = (ResultSet)cs.getObject(index); 15         List result = new ArrayList(); 16         while(rs.next()) { 17             String postype =rs.getString(1); 18             String description = rs.getString(2); 19             CodeTableItemDTO posTypeItem = new CodeTableItemDTO(); 20             posTypeItem.setCode(postype); 21             posTypeItem.setDescription(description); 22             result.add(posTypeItem); 23         } 24         return result; 25     } 26  27  28  29 4. dao方法 30     public List procPostype() { 31         String p = ""; 32         Map para = new HashMap(); 33         para.put("p",p); 34         para.put("p_ref_postypeList",null); 35          this.getSqlMapClientTemplate().queryForList("pos_dayprocset.procrefcursor",  para); 36          return (List)para.get("p_ref_postypeList"); 37     }


这个跟jdbc的方式非常的相似.
我们使用的是ibatis的2.0版本,比较麻烦。
如果是使用2.2以上版本就非常简单的。
因为可以在parameterMap中定义一个resultMap.这样就无需要自己定义处理器了。
可以从分析2.0和2.0的dtd文件知道。
上面的两种方式都是非常的复杂,如果仅仅是需要返回一个结果集,那就完全可以使用函数来实现了。 .

  1 create or replace package procpkg is  2    type refcursor is ref cursor;  3    procedure procrefcursor(p varchar2, p_ref_postypeList  out refcursor);  4    function procpostype(p varchar2) return PosTypeTable;  5 end procpkg;  6   7 create or replace package body procpkg is  8   procedure procrefcursor(p varchar2, p_ref_postypeList out  refcursor)  9   is 10     v_posTypeList PosTypeTable; 11   begin 12     v_posTypeList :=PosTypeTable();--初始化嵌套表 13     v_posTypeList.extend; 14     v_posTypeList(1) := PosType('A001','客户资料变更'); 15     v_posTypeList.extend; 16     v_posTypeList(2) := PosType('A002','团体资料变更'); 17     v_posTypeList.extend; 18     v_posTypeList(3) := PosType('A003','受益人变更'); 19     v_posTypeList.extend; 20     v_posTypeList(4) := PosType('A004','续期交费方式变更'); 21     open p_ref_postypeList for  select * from table(cast (v_posTypeList as PosTypeTable)); 22   end; 23  24   function procpostype(p varchar2) return PosTypeTable 25   as 26    v_posTypeList PosTypeTable; 27   begin 28       v_posTypeList :=PosTypeTable();--初始化嵌套表 29     v_posTypeList.extend; 30     v_posTypeList(1) := PosType('A001','客户资料变更'); 31     v_posTypeList.extend; 32     v_posTypeList(2) := PosType('A002','团体资料变更'); 33     v_posTypeList.extend; 34     v_posTypeList(3) := PosType('A003','受益人变更'); 35     v_posTypeList.extend; 36     v_posTypeList(4) := PosType('A004','续期交费方式变更'); 37     return  v_posTypeList; 38   end; 39 end procpkg;


ibatis配置

  1 <resultMap id="posTypeResultMap" class="com.palic.elis.pos.common.dto.CodeTableItemDTO">  2    <result property="code" column="posType"/>  3    <result property="description" column="description"/>  4  </resultMap>  5   6   <select id="procPostype" resultMap="posTypeResultMap">  7     select * from table(cast (procpkg.procpostype(#value#) as PosTypeTable))  8   </select>


Dao的写法跟普通查询一样

  1 public List queryPostype() {  2   return this.getSqlMapClientTemplate().queryForList("pos_dayprocset.procPostype", null);  3 }


有几点需要注意,这里不能使用索引表,而是嵌套表。
另外就是把嵌套表强制转换为普通表。









4:Oracle动态游标实现动态SQL循环遍历,和静态游标的比较。

        注: 本文 来源与:《   Oracle动态游标实现动态SQL循环遍历,和静态游标的比较  》

动态游标可以遍历动态的表,

格式:

  1 TYPE 游标类型 IS REF CURSOR;  --定义一个动态游标  2 游标名  游标类型;

如果查询的表的数据不同的,动态变化的,这时候可以用动态游标。

需要注意的是,动态游标的定义,

在普通存储过程中:需要放在 is 后面的第一行

动态游标通过:open 游标 for 字符串,形式使用,遍历。

  1 create or replace procedure P_TEST_SQL  2 is  3 TYPE ref_cursor_type IS REF CURSOR;  --定义一个动态游标       4 tablename varchar2(200) default 'ess_client';  5 v_sql varchar2(1000);  6 mobile varchar2(15);  7 usrs ref_cursor_type;  8 begin  9   --使用连接符拼接成一条完整SQL      10   v_sql := 'select usrmsisdn from '||tablename||' where rownum < 11'; 11   --打开游标      12   open usrs for v_sql ; 13   loop 14       fetch usrs into mobile; 15       exit when usrs%notfound; 16       insert into tmp(usrmsisdn) values(mobile); 17   end loop; 18   close usrs; 19   commit; 20 end P_TEST_SQL;

下面是一个自己做过的一个实例,代码只保留了可参考部分。

  1 create or replace procedure DDGZ1--订单跟踪  2 (  3        P_flag varchar,--订单类型  4        P_operate varchar,--操作类型  5        P_BH varchar --传入的编号ID  6 )  7 is  8       TYPE ref_cursor_type IS REF CURSOR;  9        v_cur_CKD  ref_cursor_type; 10  11 begin 12    open v_cur_CKD for 'select v_ERP,v_DRP,v_jxsmc,v_jxs,v_DWMC,v_czr,v_jlsj from tmp_DDGZ1'; 13    fetch v_cur_CKD into v_ERP,v_DRP,v_jxsmc,v_jxs,v_DWMC,v_czr,v_jzsj; 14    while v_cur_CKD%found 15      loop 16      insert into DRPDDGZJL2 (JLID,DRP,ERP,JXS,JXSMC,JLSJ,GZJL,CZR,BHID) values(SYS_GUID(),v_drp,v_erp,v_jxs,v_jxsmc,v_jzsj,v_DWMC||'受托代销成品库接收订单,组织发货',v_CZR,'出库单'||P_BH); 17      fetch v_cur_CKD into v_ERP,v_DRP,v_jxsmc,v_jxs,v_DWMC,v_czr,v_jzsj; 18      end loop; 19      close v_cur_CKD; 20  end;


而对于包头包体的存储过程。经常是如下形式:

在包头中:

  1 CREATE OR REPLACE PACKAGE BIA_FYJSDCX  2   3 is  --发运结算单查询  4   type T_CURSOR is ref cursor;  5   procedure ERP_HY_FYJSDCX  6   (  7     Re_CURSOR out T_CURSOR  8   );  9 end BIA_FYJSDCX;

包体中:

  1 procedure ERP_HY_FYJSDCX  2   (  3     Re_CURSOR out T_CURSOR  4   )  5 begin  6      v_sql:='select * from T_FYJSDCX4';  7       Open Re_CURSOR For v_sql;  8   9 end;


3-7:oracle存储过程异常捕获

        注: 本段内容来自于:《  oracle存储过程异常捕获  》

oracle存储过程异常捕获学习,执行及演示过程:

存储过程:

  1 CREATE OR REPLACE PROCEDURE sp_test_2  2 (  3    param1 in int,     --输入参数  4    param2 in int,  5    out_return out varchar2 --返回结果  6 )  7 is  8    --全局变量  9    val int; 10    errorException exception; --申明异常 11    errorCode number; --异常代号 12    errorMsg varchar2(1000); --异常信息 13    flag varchar2(10); 14 begin 15      flag := 'true'; 16      out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 17      val := param1/param2; 18      --/* 19      exception 20          when errorException then 21               errorCode := SQLCODE; 22               errorMsg := SUBSTR(SQLERRM, 1, 200); 23               flag := 'false'; 24               out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 25          when others then 26                errorCode := SQLCODE; 27                errorMsg := SUBSTR(SQLERRM, 1, 200); 28                flag := 'false'; 29                out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 30      --dbms_output.put_line(errorCode || ',' || errorMsg);  31      --*/ 32 end sp_test_2;

演示存储过程:

  1 DECLARE  2    out_return varchar2(1000);  3    val int; --全局变量    4    errorException exception; --申明异常    5    errorCode number; --异常编码    6    errorMsg varchar2(1000); --异常信息    7    flag varchar2(10);  8 begin  9      flag := 'true'; 10      out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 11      val := 1/0; 12      exception  --异常捕捉,不要把有需要的代码放在异常捕捉后面,有异常才会执行异常代码下所有代码,没有异常不会执行   13          when errorException then 14               errorCode := SQLCODE; 15               errorMsg := SUBSTR(SQLERRM, 1, 200); 16               flag := 'false'; 17               out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 18          when others then 19                errorCode := SQLCODE; 20                errorMsg := SUBSTR(SQLERRM, 1, 200); 21                flag := 'false'; 22                out_return := 'flag=' || flag || ',errorCode=' || errorCode || ',errorMsg=' || errorMsg; 23  24      dbms_output.put_line(out_return); 25 end;

sqlplus中执行存储过程:

  1 DECLARE  2      out_return varchar2(1000);  3 begin  4      sp_test_2(1,0,out_return);  5      dbms_output.put_line(out_return); --打印结果    6 end;


执行存储过程 1除以0 结果:

存储过程、函数、触发器和包






oracle 之定义数组类型

注:本文来源:《oracle 之定义数组类型

oracle 数组类型,没有现成的类型,但是可以自己随意定义,很方便。

Oracle 数组可以分为定长数组和可变长的数组两类。以下主要是一维数组介绍:

1:定长数组:

  1 /*定长字符数组,数组大小为10*/  2 declare  3 type v_arr is varray(10) of varchar2(30);  4 my_arr v_arr;  5 my_arr:=v_arr('1','2','3');  6 begin  7 	for i in 1..my_arr.count  8 	loop  9 		dbms_output_line(my_arr(i)); 10 	end loop; 11 end; 12 

2:变长数组:

  1 /*可变长字符数组,元素大小30,索引标号integer类型自增长*/  2 declare  3 type v_table is table of varchar2(30) index by binary_integer;  4 --类型可以是前面的类型定义,index by binary_integer子句代表以符号整数为索引,     5 --这样访问表类型变量中的数据方法就是“表变量名(索引符号整数)”。     6 my_table v_table;  7 begin  8       for i in 1..20  9      loop 10           my_table(i):=i; 11           dbms_output.put_line(my_table(i)); 12       end loop; 13 end; 14 

Oracle存储过程 数组集合的使用

注:本文来源《 Oracle存储过程 数组集合的使用  》

1 说明

1.1 RECORD

定义记录数据类型。它类似于C语言中的结构数据类型(STRUCTURE),PL/SQL提供了将几个相关的、分离的、基本数据类型的变量组成一个整体的方法,即RECORD复合数据类型。在使用记录数据类型变量时,需要在声明部分先定义记录的组成、记录的变量,然后在执行部分引用该记录变量本身或其中的成员。

定义记录数据类型的语法如下:

  1 TYPE RECORD_NAME IS RECORD(  2   3 V1 DATA_TYPE1 [NOT NULL][:=DEFAULT_VALUE],  4   5 V2 DATA_TYPE2 [NOT NULL][:=DEFAULT_VALUE],  6   7 VN DATA_TYPEN [NOT NULL][:=DEFAULT_VALUE]);  8 
1.2 VARRAY

数组是具有相同数据类型的一组成员的集合。每个成员都有一个唯一的下标,它取决于成员在数组中的位置。在PL/SQL中,数组数据类型是VARRAY(variable array,即可变数组)。

定义VARRAY数据类型的语法如下:

  1 TYPE VARRAY_NAMEIS VARRAY(SIZE) OF ELEMENT_TYPE [NOT NULL];

其中,varray_name是VARRAY数据类型的名称,size是正整数,表示可以容纳的成员的最大数量,每个成员的数据类型是element_typeo默认时,成员可以取空值,否则需要使用NOT NULL加以限制。

1.3 TABLE

定义记录表(或索引表)数据类型。它与记录类型相似,但它是对记录类型的扩展。它可以处理多行记录,类似于C语言中的二维数组,使得可以在PL/SQL中模仿数据库中的表。

定义记录表类型的语法如下:

  1 TYPE TABLE NAME IS TABLE OF ELEMENT_TYPE [NOT NULL]  2   3 INDEX BY [BINARY_INTEGER|PLS_INTEGER|VARRAY2];  4 

关键字INDEX BY表示创建一个主键索引,以便引用记录表变量中的特定行。

BINARY_INTEGER的说明

如语句:TYPE NUMBERS  IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;其作用是,加了”INDEX BYBINARY_INTEGER ”后,NUMBERS类型的下标就是自增长,NUMBERS类型在插入元素时,不需要初始化,不需要每次EXTEND增加一个空间。

而如果没有这句话“INDEXBY BINARY_INTEGER”,那就得要显示对初始化,且每插入一个元素到NUMBERS类型的TABLE中时,都需要先EXTEND。

2 举例


2.1 创建表结构以及数据准备

  1 --组织机构结构表    2 CREATE TABLE SF_ORG  3 (  4 ORG_ID INT NOT NULL, --组织机构主键ID    5 ORG_NAME VARCHAR2(50),--组织机构名称    6 PARENT_ID INT--组织机构的父级    7 )  8   9 --一级组织机构   10 INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(1, '一级部门1',0); 11  12 --二级部门   13  14 INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(2, '二级部门2',1); 15 INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(3, '二级部门3',1); 16 INSERT INTO SF_ORG(ORG_ID, ORG_NAME, PARENT_ID) VALUES(4, '二级部门4',1);


2.2 RECORD的使用举例

先定义一个只与SF_ORG表中某几个列的数据类型相同的记录数据类型TYPE_ORG_RECORD,然后声明一个该数据类型的记录变量V_ORG_RECORD,最后用替换变量&ORG_ID接受输入的雇员编码,查询并显示该雇员的这几列中的信息。注意,在使用RECORD数据类型的变量时要用“.”运算符指定记录变量名限定词。

一个记录类型的变量只能保存从数据库中查询出的一行记录,如果查询出了多行记录,就会出现错误。

  1 DECLARE  2   TYPE TYPE_ORG_RECORD IS RECORD(  3   V_ORG_NAME SF_ORG.ORG_NAME%TYPE,  4   V_PARENT_ID SF_ORG.PARENT_ID%TYPE);  5   V_ORG_RECORD TYPE_ORG_RECORD;  6 BEGIN  7   SELECT ORG_NAME,PARENT_ID INTO V_ORG_RECORD  8   FROM SF_ORG SO  9   WHERE SO.ORG_ID=&ORG_ID; 10   DBMS_OUTPUT.PUT_LINE('部门名称:' || V_ORG_RECORD.V_ORG_NAME); 11   DBMS_OUTPUT.PUT_LINE('上级部门编码:' || TO_CHAR(V_ORG_RECORD.V_PARENT_ID)); 12 END;


2.3 VARRAY的使用举例

先定义一个能保存5个VARCHAR2(25)数据类型的成员的VARRAY数据类型ORG_VARRAY_TYPE,然后声明一个该数据类型的VARRAY变量V_ORG_VARRAY,最后用与ORG_VARRAY_TYPE数据类型同名的构造函数语法给V_ORG_VARRAY变量赋予初值并显示赋值结果。

注意,在引用数组中的成员时.需要在一对括号中使用顺序下标,下标从1开始而不是从0开始。

  1 DECLARE  2   TYPE ORG_VARRAY_TYPE IS VARRAY(5) OF VARCHAR2(25);  3   V_ORG_VARRAY ORG_VARRAY_TYPE;  4 BEGIN  5   V_ORG_VARRAY := ORG_VARRAY_TYPE('1','2','3','4','5');  6   DBMS_OUTPUT.PUT_LINE('输出1:' || V_ORG_VARRAY(1) || ''|| V_ORG_VARRAY(2) || ''|| V_ORG_VARRAY(3) || ''|| V_ORG_VARRAY(4));  7   DBMS_OUTPUT.PUT_LINE('输出2:' || V_ORG_VARRAY(5));  8   V_ORG_VARRAY(5) := '5001';  9   DBMS_OUTPUT.PUT_LINE('输出3:' || V_ORG_VARRAY(5)); 10 END;



2.4 TABLE使用举例



2.4.1 存储单列多行

这个和VARRAY类似。但是赋值方式稍微有点不同,不能使用同名的构造函数进行赋值。具体的如下:

  1 DECLARE  2   TYPE ORG_TABLE_TYPE IS TABLE OF VARCHAR2(25)  3   INDEX BY BINARY_INTEGER;  4   V_ORG_TABLE ORG_TABLE_TYPE;  5 BEGIN  6   V_ORG_TABLE(1) := '1';  7   V_ORG_TABLE(2) := '2';  8   V_ORG_TABLE(3) := '3';  9   V_ORG_TABLE(4) := '4'; 10   V_ORG_TABLE(5) := '5'; 11   DBMS_OUTPUT.PUT_LINE('输出1:' || V_ORG_TABLE(1) || ''|| V_ORG_TABLE(2) || ''|| V_ORG_TABLE(3) || ''|| V_ORG_TABLE(4)); 12   DBMS_OUTPUT.PUT_LINE('输出2:' || V_ORG_TABLE(5)); 13 END;


2.4.2 存储多列多行和ROWTYPE结合使用

采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。

  1 DECLARE  2    TYPE T_TYPE IS TABLE OF SF_ORG%ROWTYPE;  3    V_TYPE  T_TYPE;  4  BEGIN  5     SELECT ORG_ID,ORG_NAME,PARENT_ID BULK COLLECT INTO V_TYPE  6     FROM SF_ORG  7     WHERE SF_ORG.ORG_ID <= 3;  8   9     FOR V_INDEX IN V_TYPE.FIRST .. V_TYPE.LAST LOOP 10         DBMS_OUTPUT.PUT_LINE(V_TYPE(V_INDEX).C1 || ' ' || V_TYPE(V_INDEX).C2); 11     END LOOP; 12  END;


2.4.3 存储多列多行和RECORD结合使用

采用bulkcollect可以将查询结果一次性地加载到collections中。而不是通过cursor一条一条地处理。

  1 DECLARE  2    TYPE TEST_EMP IS RECORD  3    (  4     C1 SF_ORG.ORG_NAME%TYPE,  5     C2 SF_ORG.PARENT_ID%TYPE  6    );  7    TYPE T_TYPE IS TABLE OF TEST_EMP;  8    V_TYPE  T_TYPE;  9  BEGIN 10     SELECT ORG_NAME,  PARENT_ID BULK COLLECT INTO V_TYPE 11     FROM SF_ORG 12     WHERE SF_ORG.ORG_ID <= 3; 13  14     FOR V_INDEX IN V_TYPE.FIRST .. V_TYPE.LAST LOOP 15         DBMS_OUTPUT.PUT_LINE(V_TYPE(V_INDEX).C1 || ' ' || V_TYPE(V_INDEX).C2); 16     END LOOP; 17  END;















Oracle存储过程自定义数组定义与使用

最近为公司项目数据库通过存储过程做归档,需要用到自定义数组

百度结果中很多写的都不是很清晰,可变长数组定义好后,如何使用。

在此做个记录:

定义:

type id_array is table of number(15) index by binary_integer;

acb_ids   id_array;

这个定义方式适用在package,如果是纯粹在存储过程中自定义类型请百度 。

使用:

acb_ids(acb_ids.count+1) := c_account_books_cbs_rec.acb_id;

网上很多实例是这样的:acb_ids(acb_ids.count) := c_account_books_cbs_rec.acb_id;这样写是没有用的,因为这个时候acb_ids是空的,那么acb_ids.count也是无效的,acb_ids在使用时回会报"未找到任何数据",因此需要acb_ids.count+1。

c_account_books_cbs_rec为游标遍历中的一个对象,上述代码意思是将游标数据中每行记录的ID放到自定义数组中,方便存储过程的值返回或者游标遍历外程序体中使用。

for x in 1 .. acb_ids.count loop
        do something;
end loop;



存储过程中定义参数类型为数组

注意:本文来源:《存储过程中定义参数类型为数组

1:存储过程

  1 Procedure Update_Batch_Id(p_entity_id in Number,  2                             p_vdr_id    In fnd_table_of_number) is  3   begin  4   5     For i In 1 .. p_vdr_id.COUNT Loop  6       update cux_table_header cvs  7          set cvs.attribute10 = p_entity_id  8        where cvs.header_id = p_vdr_id(i);  9     End Loop; 10   end;

2:JAVA中调用

  1 List list = new ArrayList();  2 ...  3 list.add(row.getHeaderId());  4 ...  5   6   7  OracleCallableStatement statement = null;  8         OracleConnection oracleConnection = (OracleConnection)tsn.getJdbcConnection();  9 int size = list.size(); 10 if (size>0) 11         { 12            Number[] vdrIdArray = (Number[])list.toArray(new Number[size]); 13             ARRAY vdrArray=null; 14 try { 15                           ArrayDescriptor tableOfNumber = 16                               oracle.sql.ArrayDescriptor.createDescriptor("FND_TABLE_OF_NUMBER", 17                                                                           oracleConnection); 18                          vdrArray = new ARRAY(tableOfNumber, oracleConnection, vdrIdArray); 19                        String sql = 20                            "BEGIN cux_XXXXXXX_pkg.Update_Batch_Id(:1,:2);end;"; 21                        statement = (OracleCallableStatement)oracleConnection.prepareCall(sql); 22  23                        statement.setObject(1, batchid); 24                        statement.setARRAY(2, vdrArray); 25                        statement.execute(); 26                       }catch (Exception ex) { 27                                String[][] stra2 = { { "123456wewee", ex.getMessage() }, }; 28                                LogUtil.of(stra2, this).print(pageContext); 29                             ex.printStackTrace(); 30                             System.out.println(ex.getMessage()); 31                            } 32 }


























——————————————————————————————————————————————————————————————————————————————————————————














二:函数

存储过程、函数、触发器和包

1:创建函数

存储过程、函数、触发器和包存储过程、函数、触发器和包

  1 create or replace function get_avg_pay(num_deptno number )return number is /*创建一个函数,该函数实现计算某个部门的平均工资,传入部门编号参数*/  2   num_avg_pay number; /*保存平均工资的内部变量*/  3 begin  4    select avg(sal) into num_avg_pay from emp where deptno=num_deptno;/*某个部门的平均工资*/  5    return (round(num_avg_pay));  6 exception  7    when no_data_found then  /*如果此部门编号不存在*/  8       dbms_output.put_line('该部门编号不存在');  9     return(0); /*返回平均工资0*/ 10 end; 11 /

存储过程、函数、触发器和包

2:调用函数

存储过程、函数、触发器和包

  1 set serveroutput on  2 declare  3   avg_pay number ;/*定义变量,储存函数返回值*/  4 begin  5   avg_pay :=get_avg_pay(10);  6   dbms_output.put_line('平均工资是:'||avg_pay);  7 end;  8 /  9 

存储过程、函数、触发器和包

3:删除函数

存储过程、函数、触发器和包

  1 drop function get_avg_pay;  2   3 

存储过程、函数、触发器和包




三:触发器

存储过程、函数、触发器和包


1:触发器钙素

存储过程、函数、触发器和包存储过程、函数、触发器和包


存储过程、函数、触发器和包



2:语句级触发器

存储过程、函数、触发器和包存储过程、函数、触发器和包

  1   2 /*在scott模式下 创建dept_log数据表,并在其中定义连个字段,分别用来 3 储存操作种类信息和操作日期*/  4 create table dept_log  5 (  6    operate_tag varchar2(10),/*定义字段,储存操作种类信息*/  7    operate_time date /*定义字段,储存操作日期*/  8 );  9  10  11 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 /*创建一个触发器 tri_dept,该触发器在 insert、update、delete 事件下 3 都可以被触发,并且操作的数据对象是dept,要求在触发器执行时输出对dept表所做的具体操作*/  4 create or replace trigger tri_dept  5 before insert or update or delete  6 on dept /*创建触发器,当dept表发生插入、修改、删除等操作时引起的触发器执行*/  7 declare  8    var_tag varchar2(10);  9 begin 10    if inserting then /*当触发器事件是 insert 时*/ 11           var_tag:='插入'; 12     elsif updating then /*当触发器事件是 update 时*/ 13            var_tag:='修改'; 14     elsif deleting then  /*当触发事件是delete时*/ 15            var_tag:='删除'; 16     end if; 17 insert into dept_log values(var_tag,sysdate); 18 end tri_dept; 19 /

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1 insert into  dept values(66,'adfasdf','fsdafd');  2 update dept set loc='w235f' where deptno=66;  3 delete from dept where deptno=66;  4 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包


3:行级别触发器

存储过程、函数、触发器和包

  1 /*在scott模式下,创建一个用于储存商品种类的数据表,其中包括商品序号和商品名称*/  2 create table goods(  3   id int primary key,  4   good_name varchar2(50)  5 );

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 /*使用create sequence语句创建一个序列,命名为seq_id */  3 create sequence seq_id;  4 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1 /*创建一个行级别触发器,该触发器在数据表goods插入数据时被触发,并且在该触发器的主体中实现设置 2 goods表的id列的值。*/  3 create or replace trigger tri_insert_good  4 before insert  5   on goods /*关于goods 数据表,在向其插入新记录之前,引发该触发器的运行*/  6   for each row /*创建行触发器*/  7 begin  8    select  seq_id.nextval into :new.id from dual; /*从序列号中生成 一个新的数值,赋值给当前插入的行的id*/  9 end; 10 / 11 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 /*向goods表中插入两条记录,其中一条记录不指定id列的值,由序列seq_id来产生;另一条记录指定id的值*/  3 insert into goods(good_name) values('asdfasdf');  4 insert into goods(id,good_name) values(9,'asdtet');  5   6 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包

4:替换触发器

存储过程、函数、触发器和包

  1 With the Partitioning, OLAP, Data Mining and Real Application Testing options  2 [oracle@localhost ~]$ sqlplus / as sysdba;  3   4 SQL*Plus: Release 11.2.0.3.0 Production on Sun Jan 7 16:38:32 2018  5   6 Copyright (c) 1982, 2011, Oracle.  All rights reserved.  7   8   9 Connected to: 10 Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - Production 11 With the Partitioning, OLAP, Data Mining and Real Application Testing options 12  13 SYS@orcl> alter user system identified by oracle; 14  15 User altered. 16  17 SYS@orcl> conn system/oracle; 18 Connected. 19 SYSTEM@orcl> grant create view to scott; 20  21 Grant succeeded. 22  23 SYSTEM@orcl> conn scott/scott; 24 Connected. 25 SQL> create view view_emp_dept 26   2  as 27   3  select empno,ename,dept.deptno,dept.dname,job,hiredate from emp,dept where emp.deptno=dept.deptno; 28  29 View created 30 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 create or replace trigger tri_insert_view  3   instead of insert  4   on view_emp_dept    /*创建一个关于 view_emp_dept视图的替换触发器*/  5   for each row  /*行级别视图*/  6 declare  7    row_dept dept%rowtype;  8 begin  9   select * into  row_dept from dept where deptno =:new.deptno;/*查询指定部门编号的记录行*/ 10  if sql%notfound then 11      insert into dept(deptno,dname) values(:new.deptno,:new.dname);/*向dept表中插入数据*/ 12   end if; 13   insert into emp(empno,ename,deptno,job,hiredate) 14   values(:new.empno,:new.ename,:new.deptno,:new.job,:new.hiredate);/*向emp表中插入数据*/ 15 end tri_insert_view; 16 / 17 

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包


存储过程、函数、触发器和包


5:用户事件触发器

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1   2 /* 3  常见用户事件: 4  create 、alter 、drop、analyze、comment、grant、revoke、rename、truncate、uspend、logon、logoff 5  使用create table 语句创建一个日志信息表,该表保存的日志信息包括数据对象、数据对象类型、操作行为、操作用户 等操作日期等。 6 */  7 create table ddl_oper_log(  8   db_obj_name varchar2(20),/*数据对象名称*/  9   db_obj_type varchar2(20),/*数据对象类型*/ 10   oper_action varchar2(20),/*操作行为*/ 11   oper_user varchar2(20),/*操作用户*/ 12   oper_date   date /*操作日期*/ 13 );

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1 create or replace trigger tri_ddl_oper  2   3 /*关于scott用户的ddl 操作,(这里包括 create alter  drop ) 4 创建一个触发器,然后讲DDL操作的相关信息插入到 ddl_oper_log 日志表中*/  5        before create or alter or drop  6        on scott.schema /*在scott模式下,在创建、修改、删除数据库对象之前将引发触发器运行*/  7 begin  8       insert into ddl_oper_log values(  9         ora_dict_obj_name,/*操作的数据对象名称*/ 10         ora_dict_obj_type ,/*操作的数据对象类型 */ 11         ora_sysevent,/*系统事件名称*/ 12         ora_login_user,/*登录用户*/ 13         sysdate  ); 14  15 end; 16 /

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 SQL> create table tb_test(id number);
2
3 Table created
4
5 SQL> create view view_test as select empno,ename from emp;
6
7 View created
8
9 SQL> alter table tb_test add (name varchar2(10));
10
11 Table altered
12
13 SQL> drop view view_test;
14
15 View dropped
16
17 SQL> select * from ddl_oper_log;
18
19 DB_OBJ_NAME DB_OBJ_TYPE OPER_ACTION OPER_USER OPER_DATE
20 -------------------- -------------------- -------------------- -------------------- -----------
21 TB_TEST TABLE CREATE SCOTT 2018/1/7 21
22 VIEW_TEST VIEW CREATE SCOTT 2018/1/7 21
23 TB_TEST TABLE ALTER SCOTT 2018/1/7 21
24 VIEW_TEST VIEW DROP SCOTT 2018/1/7 21
25
View Code

存储过程、函数、触发器和包

存储过程、函数、触发器和包


四:程序包

存储过程、函数、触发器和包


1:程序包的规范

存储过程、函数、触发器和包


存储过程、函数、触发器和包存储过程、函数、触发器和包
  1 
2 /*创建一个程序包的规范,首先在该程序包中声明一个可以获取指定部门的平均工资的函数,
3 然后在声明一个可以实现按照指定比例上调职务的工资的储存过程*/

4 create or replace package pack_emp is
5 function fun_avg_sal(num_deptno number) return number;/*获取指定部门的平均工资*/
6 procedure pro_regulate_sal(var_job varchar2,num_proportion number) ;/*按照指定比例上调指定职务的工资*/
7 end pack_emp;
8 /
View Code

存储过程、函数、触发器和包


2:程序包主体

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

存储过程、函数、触发器和包

  1 create or replace package body pack_emp is  2   function fun_avg_sal(num_deptno number) return number is  --引入“规范”中的函数
3 num_avg_sal number;--定义内部变量
4 begin
5 select avg(sal)
6 into num_avg_sal
7 from emp
8 where deptno = num_deptno;--计算某个部门的平均工资
9 return(num_avg_sal);--返回平均工资
10 exception
11 when no_data_found then--若未发现记录
12 dbms_output.put_line('该部门编号不存在雇员记录');
13 return 0;--返回0
14 end fun_avg_sal;
15
16 procedure pro_regulate_sal(var_job varchar2,num_proportion number) is--引入“规范”中的存储过程
17 begin
18 update emp
19 set sal = sal*(1+num_proportion)
20 where job = var_job;--为指定的职务调整工资
21 end pro_regulate_sal;
22 end pack_emp;
23 /
24
25
26
27
28
29
30
31
32
33
34
35
36
37

存储过程、函数、触发器和包

  1 set serveroutput on  2 declare  3   num_deptno emp.deptno%type;--定义部门编号变量  4   var_job emp.job%type;--定义职务变量  5   num_avg_sal emp.sal%type;--定义工资变量  6   num_proportion number;--定义工资调整比例变量  7 begin  8   num_deptno:=10;--设置部门编号为10  9   num_avg_sal:=pack_emp.fun_avg_sal(num_deptno);--计算部门编号为10的平均工资 10   dbms_output.put_line(num_deptno||'号部门的平均工资是:'||num_avg_sal);--输出平均工资 11  12   var_job:='SALESMAN';--设置职务名称 13   num_proportion:=0.1;--设置调整比例 14   pack_emp.pro_regulate_sal(var_job,num_proportion);--调整指定部门的工资 15 end; 16 / 17 

存储过程、函数、触发器和包















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