zoukankan      html  css  js  c++  java
  • 存储过程、函数、触发器和包

    一:前言:

    image

    一:存储过程

    image

    1:创建储存过程

    image

      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

    image

    image

      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

    image

    image

    image

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

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

    2:存储过程的参数

    image

    2-1:in 模式参数

    image

      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

    image

    image

    image

      1 begin
      2  insert_dept(var_ename=>'asadad',var_loc=>'asdfasd',num_deptno=>15) ;
      3 end;
      4 /
    View Code

    image

    image

    image

      1 begin
      2    insert_dept(28,'asdfa','fasdf');
      3 end;
      4 /
      5 
    View Code

    image

    image

    image

      1 SQL> exec insert_dept(38,var_loc=>'adfasd',var_ename=>'adfasd');
      2 
      3 PL/SQL procedure successfully completed
    View Code

    image

    2-2: out 模式参数

    image

      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

    image

    image

    image

      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

    image

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

    image

      1 variable var_dname varchar2(50);
      2 variable var_loc varchar2(50);
      3 exec select_dept(15,:var_dname,:var_loc );
      4 
    View Code

    image

    image

    image

    image

    image

    2-3:in  out 模式参数

    image

      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 /

    image

    image

    image

      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 

    image

    image

    2-4:in 参数的默认值

    image

      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 

    image

    image

    image

      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 /

    image

    image

    3:存储过程示例:

     

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

    F[PH69FQ[W}9`U)PB~ZU7D9

      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循环

    image

      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+"	 ename:"+ename+"	 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 }

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

    二:函数

    image

    1:创建函数

    imageimage

      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 /

    image

    2:调用函数

    image

      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 

    image

    3:删除函数

    image

      1 drop function get_avg_pay;
      2 
      3 

    image

    三:触发器

    image

    1:触发器钙素

    imageimage

    image

    2:语句级触发器

    imageimage

      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 

    image

    image

      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 /

    image

    image

    image

      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 

    image

    image

    image

    3:行级别触发器

    image

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

    image

    image

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

    image

    image

      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 

    image

    image

      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 

    image

    image

    image

    imageimage

    4:替换触发器

    image

      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 

    image

    image

      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 

    image

    image

    image

    image

    5:用户事件触发器

    image

    image

      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 );

    image

    image

      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 /

    image

    image

    image

      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

    image

    image

    四:程序包

    image

    1:程序包的规范

    image

      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

    image

    2:程序包主体

    image

    image

    image

    image

      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 

    image

      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 

    image

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

  • 相关阅读:
    ATOM编辑器插件
    说说关于IE浏览器兼容性
    git命令
    Vue js 的生命周期详解
    Flexbox 布局教程
    到底vuex是什么?
    CSS3 动画 animation和@keyframes
    zabbix添加触发器Triggers
    zabbix邮件报警
    修改zabbix监控项刷新时间
  • 原文地址:https://www.cnblogs.com/ios9/p/8214275.html
Copyright © 2011-2022 走看看