您的位置:永利集团登录网址 > 计算机入门 > Oracle存储过程使用技巧与经验总结

Oracle存储过程使用技巧与经验总结

2019-09-30 23:21

上面包车型大巴见地是在动用游标的长河中做的日志。小编也是率先次采纳,就算有怎么着难堪的地方请斟酌指正,大家共同尽力。

  一,定义:Sql Server的囤积进程是三个被命名的仓库储存在服务器上的Transacation-Sql语句群集,是包装重复性职业的一种方法.

oracle存储过程常用手艺

1.

  二,存款和储蓄进程的帮助和益处:

大家在张开pl/sql编制程序时打交道最多的就是积累进程了。存款和储蓄进程的布局是充裕的粗略的,大家在那边除了学习存储进度的着力协会外,还大概会学习编写存款和储蓄进度时有关的一对实用的学识。如:游标的管理,至极的处理,群集的精选等等

  消息 16951,级别 16,状态 1,过程 usp_proc,第 16 行
      变量 '@myref' 不可能用作参数,因为在举行该进程前,不得为 CU奥德赛SO奥迪Q5 OUTPUT 参数分配游标。

   1,重复使用。存款和储蓄进程能够重复使用,进而得以减小数据库开荒职员的专业量。

1.积存进度结构

  那个标题是本身在调用叁个递归的、输出cursor output 的积存进程

   2,进步品质。存款和储蓄进度在成立的时候就实行了编译,以往应用的时候不要再另行编写翻译。经常的SQL语句每试行贰次就必要编写翻译三回,所以选用存款和储蓄进程进步了功效。

1.1 第叁个存款和储蓄进程

create proc usp_proc(
@level int
@myref cursor varying output
)
as
begin
    if @level=3
        begin
             set @myref=cursor local static for
            select * from table
            open @myref
        end
     if @level<3
        begin
        declare @cur cursor
        exec usp_proc 2 @cur output --递归
        --
        --对输出游标@cur做一些操作
        --
        --使用完游标
        close @cur  --关闭游标
        deallocate @cur --删除游标
        end
end            

     3,收缩互联网流量。存储进度位于服务器上,调用的时候只须求传递存款和储蓄进程的称呼以及参数就能够了,因此裁减了互连网传输的数据量。

create or replace procedure proc1(  

若果未有对输出的游标做close、deallocate管理就能并发上边错误。

   4,安全性。参数化的积存进程可避防御SQL注入式的口诛笔伐,何况能够将Grant、Deny以及Revoke权限应用于积存进度。

 p_para1 varchar2,  

2.

  三,语法,创建存款和储蓄进程:  

 p_para2 out varchar2,  

  未有为@cur,分配游标

语法

 p_para3 in out varchar2  

  这一个标题是本人在选用存储进度再次回到的游标 cursor output 发生的

CREATE PROC[ EDURE ] [ owner**. ] procedure_name [ ; number ]     [ { @parameter data_type }         [ VARYING ] [ = default ] [ OUTPUT ]     ] [ ,...n ] [ WITH     { RECOMPILE | ENCRYPTION | RECOMPILE ,* ENCRYPTION } ] [ FOR REPLICATION ] AS sql_statement [ ...n* ]

参数

owner

    具备存款和储蓄进度的顾客 ID 的名号。owner 必得是当前客商的称谓或当前客户所属的剧中人物的名称。

procedure_name

    新存储进度的称号。进程名必需切合标志符准则,且对于数据库及其主人必得独一。

;*number*

    是可选的板寸,用来对同名的经过分组,以便用一条 DROP PROCEDURE 语句就能够将同组的历程一同除去。譬喻,名叫 orders 的应用程序使用的进程可以命名字为 orderproc;1、orderproc;2 等。DROP PROCEDURE orderproc 语句将除了整个组。假设名称中含有定界标记符,则数字不应富含在标记符中,只应在 procedure_name 前后使用方便的定界符。

@parameter

    进度中的参数。在 CREATE PROCEDURE 语句中得以声美素佳儿个或多少个参数。客商必需在实行进程时提供每种所注解参数的值(除非定义了该参数的默许值,或许该值设置为等于另一个参数)。存款和储蓄进度最多能够有 2.100 个参数。

行使 @ 符号作为第三个字符来钦赐参数名称。参数名称必得切合标志符的平整。每一种进度的参数仅用于该进度自身;同样的参数名称能够用在其余进程中。私下认可意况下,参数只好代替常量,而不能够用于代替表名、列名或任何数据库对象的称谓。

data_type

    参数的数据类型。除 table 之外的任何具备数据类型均能够作为存款和储蓄进程的参数。然则,cursor 数据类型只好用来 OUTPUT 参数。假使钦定 cursor 数据类型,则还必需钦赐VARubiconYING 和 OUTPUT 关键字。对于能够是 cursor 数据类型的出口参数,未有最大数量的限量。

VARYING

    钦赐作为出口参数帮助的结果集(由存款和储蓄进程动态构造,内容能够扭转)。仅适用于游标参数。

default

    参数的暗中同意值。假设定义了暗中认可值,不必内定该参数的值就能够进行进度。暗许值必须是常量或 NULL。假设经过将对该参数使用 LIKE 关键字,那么私下认可值中得以包涵通配符(%、_、[] 和 [^])。

OUTPUT

    申明参数是回来参数。该选用的值能够回来给 EXEC[UTE]。使用 OUTPUT 参数可将音讯再次回到给调用进程。Textntextimage 参数可用作 OUTPUT 参数。使用 OUTPUT 关键字的输出参数能够是游标占位符。

n

    表示最多能够内定 2.100 个参数的占位符。

{RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}

    RECOMPILE 证明 SQL Server 不会缓存该进程的布署,该进度就要运转时再次编写翻译。在行使非规范值或一时值而不希望覆盖缓存在内部存款和储蓄器中的实践布署时,请使用 RECOMPILE 选项。

ENCRYPTION 表示 SQL Server 加密 syscomments 表中蕴藏 CREATE PROCEDURE 语句文本的条目。使用 ENC奥迪Q5YPTION 可卫戍将经过作为 SQL Server 复制的一有的公布。

FOR REPLICATION

    钦赐无法在订阅服务器上实行为复制创设的积攒进程。.使用 FO中华V REPLICATION 选项成立的囤积进度可用作存款和储蓄进程筛选,且只可以在复制进度中举办。本选项不可能和 WITH RECOMPILE 选项一同行使。

AS

   钦赐进程要推行的操作。

sql_statement

   进程中要含有的妄动数目和花色的 Transact-SQL 语句。但有一点限制。

n

   是意味此进度能够分包多条 Transact-SQL 语句的占位符。

  四,使用方法:

  

**********************************************

注:*所包围部分来自MS的协同丛书.

 

                           多少个实例

                        (AjaxCity表中剧情)

     ID        CityName   Short

             1         苏州市     SZ  

             2     无锡市     WX

             3         常州市     CZ

1.取舍表中保有剧情并回到二个数据集

        CREATE PROCEDURE mysp_All
        AS
           select * from AjaxCity
        GO

实施结果

        图片 1

2.基于传入的参数举行查询并赶回二个数据集

       CREATE PROCEDURE mysp_para
            @CityName varchar(255),

            @Short    varchar(255)
       AS
         select * from AjaxCity where CityName=@CityName And Short=@Short
       GO

实践结果

        图片 2

3.含有输出参数的仓库储存进度(再次来到前两条记下的ID的和)

CREATE PROCEDURE mysp_output
       @SUM int  output
 AS
       select @SUM=sum([ID]) from (select top 2 * from AjaxCity) as tmpTable
GO

施行结果

         图片 3

4.在蕴藏进程中运用游标

  有这么贰个表,存款和储蓄的是各超阶级市上面包车型客车县级市的消息.如图:

   图片 4

   未来想总计出各类地级市上边包车型客车县级市的个数,并构成贰个字符串.结果应该是"5,2,2".

 

CREATE PROCEDURE mysp_Cursor
    @Result varchar(255) output//注明输出变量
AS
    declare city_cursor cursor for//表明游标变量
    select [ID] from AjaxCity

set @Result=''
declare @Field int//表明一时存放CityID的变量
open city_cursor //张开游标
fetch next from city_cursor into @Field//将实际ID赋给变量
while(@@fetch_status=0)//循环起来
begin
       if @Result = ''
           select @Result = convert(nvarchar(2),count(*))  from AjaxCounty where CityID=@Field
       else
           select @Result = @Result + ',' + convert(nvarchar(2),count(*)) from AjaxCounty where CityID=@Field
      
       fetch next from city_cursor into @Field//下一个CityID
end
close city_cursor//关闭游标
deallocate city_cursor//释放游标引用
GO

 

进行结果

       图片 5

 

    好了,关于存款和储蓄进度先写到这里.以上多少个例证基本上完毕了平庸所用到的大多数功用.至于复杂的蕴藏进程,所用到的领悟根本是SQL的语法,以及SQL中放置函数的使用.已不属于本文所要研究的范围了.

)as    

  

v_name varchar2(20);  

create proc myproc(
@mycur cursor varying output
)
as
begin
set @mycur=cursor local static  for
select * from table

open @mycur --打开游标
end

--调用myproc
declare @cur cursor
exec myproc @cur output
fetch next from @cur
while @@fetch_status=0
    begin
    --使用游标
    fetch next from @cur
    end 

begin  

出现上述错的开始和结果正是概念游标后要求打开 open @mycur

 v_name := '张三丰';  

 p_para3 := v_name;  

 dbms_output.put_line('p_para3:'||p_para3);  

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

create or replace procedure proc1(  

 p_para1 varchar2,  

 p_para2 out varchar2,  

 p_para3 in out varchar2  

)as    

v_name varchar2(20);  

begin  

 p_para1 :='aaa';  

 p_para2 :='bbb';  

 v_name := '张三丰';  

 p_para3 := v_name;  

 dbms_output.put_line('p_para3:'||p_para3);  

 null;  

end;  

     

Warning: Procedure created with compilation errors  

 

SQL> show error;  

Errors for PROCEDURE LIFEMAN.PROC1:  

 

LINE/COL ERROR  


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

8/3      PLS-00363: expression 'P_PARA1' cannot be used as an assignment target  

8/3      PL/SQL: Statement ignored  

那或多或少与其它高端语言都不可同日而语。它也等于java在参数前边加上final关键字。

OUT 参数:作为出口参数,要求介意,当二个参数被钦点为OUT类型时,就算在调用存款和储蓄进程在此以前对该参数进行了赋值,在蕴藏进度中该参数的值依然是null.

create or replace procedure proc1(  

 p_para1 varchar2,  

 p_para2 out varchar2,  

 p_para3 in out varchar2  

)as    

v_name varchar2(20);  

begin  

 v_name := '张三丰';  

 p_para3 := v_name;  

 dbms_output.put_line('p_para1:'||p_para1);  

 dbms_output.put_line('p_para2:'||p_para2);  

 dbms_output.put_line('p_para3:'||p_para3);  

end;  

 

SQL> var p1 varchar2(10);  

SQL> var p2 varchar2(10);  

SQL> var p3 varchar2(10);  

SQL> exec :p1 :='aaaa';  

SQL> exec :p2 :='bbbb';  

SQL> exec :p3 :='cccc';  

SQL> exec proc1(:p1,:p2,:p3);  

p_para1:aaaa  

p_para2:  

p_para3:张三丰  

SQL> exec dbms_output.put_line(:p2);  

 

 

PL/SQL procedure successfully completed  

p2  

---------  

INOUT 是确实的按援用传递参数。就能够作为传播参数也足以当作传播参数。

1.3 存款和储蓄进度参数宽度  

create or replace procedure proc1(  

 p_para1 varchar2,  

 p_para2 out varchar2,  

 p_para3 in out varchar2  

)as    

v_name varchar2(2);  

begin  

 v_name := p_para1;  

end;  

 

SQL> var p1 varchar2(10);  

SQL> var p2 varchar2(20);  

SQL> var p3 varchar2(30);  

SQL> exec :p1 :='aaaaaa';  

SQL> exec proc1(:p1,:p2,:p3);  

     

     

ORA-06502: PL/SQL: numeric or value error: character string buffer too small  

ORA-06512: at "LIFEMAN.PROC1", line 8  

ORA-06512: at line 1  

第一,我们要驾驭,大家不能在蕴藏进度的概念中钦命期存款款和储蓄参数的大幅,也就招致了作者们鞭长莫及在仓库储存进度中央调节制传入变量的上升的幅度。这一个增长幅度是全然由外界传入时间调控制的。

笔者们再来看看OUT类型的参数的宽度。

create or replace procedure proc1(  

 p_para1 varchar2,  

 p_para2 out varchar2,  

 p_para3 in out varchar2  

)as    

v_name varchar2(2);  

begin  

 p_para2 :='aaaaaaaaaaaaaaaaaaaa';  

end;  

SQL> var p1 varchar2(1);  

SQL> var p2 varchar2(1);  

SQL> var p3 varchar2(1);  

SQL> exec :p2 :='a';  

SQL> exec proc1(:p1,:p2,:p3);  

在该进度中,p_para2被赋予了18个字符a.

而在外部的调用进程中,p2那么些参数仅仅被定义为varchar2(1).

而把p2作为参数调用这么些进度,却并不曾报错。何况它的真实值正是二十一个a

SQL> select dump(:p2) from dual;  

DUMP(:P2)  


 

Typ=1 Len=20: 97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97  

p2  

---------  

aaaaaaaaaaaaaaaaaaaa  

     

   再来看看IN OUT参数的增长幅度  

create or replace procedure proc1(  

 p_para1 varchar2,  

 p_para2 out varchar2,  

 p_para3 in out varchar2  

)as    

v_name varchar2(2);  

begin  

 p_para3 :='aaaaaaaaaaaaaaaaaaaa';  

end;  

 

SQL> var p1 varchar2(1);  

SQL> var p2 varchar2(1);  

SQL> var p3 varchar2(1);  

SQL> exec proc1(:p1,:p2,:p3);  

试行那么些进度,依旧准确执行。

足见,对于IN参数,其宽度是由外界调控。

对此OUT 和IN OUT 参数,其宽度是由存款和储蓄进程里面调控。

据此,在写存款和储蓄进程时,对参数的拉长率实行求证是可怜有必不可缺的,最明智的艺术正是参数的数据类型使用%type。那样双方就达到了同样。

1.3 参数的暗许值

储存进程的参数能够设置私下认可值

create or replace procedure procdefault(p1 varchar2,  

                                       p2 varchar2 default 'mark')  

as    

begin  

 dbms_output.put_line(p2);  

end;  

 

SQL> set serveroutput on;  

SQL> exec procdefault('a');  

mark

能够通过default 关键字为存款和储蓄进度的参数钦命暗中同意值。在对存款和储蓄进程调用时,就能够省略暗许值。

亟待在意的是:默许值仅仅扶助IN传输类型的参数。OUT 和 IN OUT不能够钦点私下认可值

对于有暗中认可值的参数不是排在最终的图景。

create or replace procedure procdefault2(p1 varchar2 default 'remark',  

                                       p2 varchar2 )  

as    

begin  

 dbms_output.put_line(p1);  

end;  

首先个参数有暗中认可值,第叁个参数未有。借使大家想利用第贰个参数的暗许值时

exec procdefault2('aa');

如此那般是会报错的。

那怎么变吗?可以钦定参数的值。

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

remark

如此就OK了,内定aa传给参数p2

  1. 仓库储存进程里面块

2.1 内部块

我们通晓了蕴藏进程的组织,语句块由begin开端,以end截至。这几个块是足以嵌套。在语句块中能够嵌套任何以下的块。

Declare … begin … exception … end;  

create or replace procedure innerBlock(p1 varchar2)  

as    

 o1 varchar2(10) := 'out1';  

begin  

 dbms_output.put_line(o1);  

 declare    

   inner1 varchar2(20);  

 begin  

   inner1 :='inner1';  

   dbms_output.put_line(inner1);  

 

   declare    

     inner2 varchar2(20);  

   begin  

     inner2 := 'inner2';  

     dbms_output.put_line(inner2);  

   end;  

 exception    

   when others then  

     null;  

 end;  

end;  

亟待小心变量的成效域。

3.积累进度的常用技艺

3.1 哪一种集结?

我们在采纳存款和储蓄进度的时候常常索要管理记录集,相当于多条数据记录。分为单列多行和多列多行,那几个品种都足以称之为集结类型。咱们在这里举行相比较这个集中类型,以便于在编制程序时做出科学的精选。

索引表,也称之为pl/sql表,不可能积存于数据库中,成分的个数未有限定,下标可感觉负值。

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

v_student t_table;  

varchar2(20)表示贮存成分的数据类型,binary_integer表示元素下标的数据类型。

嵌套表,索引表没有 index by子句就是嵌套表,它能够存放于数据中,成分个数Infiniti,下标从1始发,况且需求初叶化

type t_nestTable is table of varchar2(20);  

v_class t_nestTable ;  

仅是这般申明是不可能运用的,必需对嵌套表实行开始化,对嵌套表举行开首化能够运用它的构造函数

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

变长数组,变长数组与高等语言的数组类型极度相似,下标以1起初,元素个数有限。

type t_array is varray (20) of varchar2(20);  

varray(20)就定义了变长数组的最大意素个数是贰10个

变长数组与嵌套表同样,也得以是数据表列的数据类型。

同期,变长数组的采取也亟需事先最初化。

项目 可存款和储蓄于数据库 元素个数 是或不是需开头化 初叶下标值

索引表 否 无限 不需

嵌套表 可 无限 需 1

可变数组 可 有限(自定义) 需 1

不问可见,假设仅仅是在积累进度中作为集结变量使用,索引表是最棒的抉择。

3.2 采纳何种游标?

来得游标分为:普通游标,参数化游标和游标变量两种。

上面以八个经过来进展求证

create or replace procedure proccursor(p varchar2)  

as    

v_rownum number(10) := 1;  

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;  

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;  

v_postype varchar2(20);  

begin  

 open c_postype;  

 fetch c_postype into v_postype;  

 dbms_output.put_line(v_postype);  

 close c_postype;  

 open c_postype1;  

 fetch c_postype1 into v_postype;  

 dbms_output.put_line(v_postype);  

 close c_postype1;  

 open c_postype2(1);  

 fetch c_postype2 into v_postype;  

 dbms_output.put_line(v_postype);  

 close c_postype2;  

 open c_postype3 for select pos_type from pos_type_tbl where rownum =1;  

 fetch c_postype3 into v_postype;  

 dbms_output.put_line(v_postype);  

 close c_postype3;  

end;  

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编制程序时,平常须要循环读取结果集的数码。进行逐行管理,这几个进度就必要对游标实行巡回。对游标进行巡回的格局有多样,我们在此一一深入分析。

create or replace procedure proccycle(p varchar2)  

as    

cursor c_postype is select pos_type, description from pos_type_tbl where rownum < 6;  

v_postype varchar2(20);  

v_description varchar2(50);  

begin  

open c_postype;  

 if c_postype%found then  

   dbms_output.put_line('found true');  

 elsif c_postype%found = false then  

   dbms_output.put_line('found false');  

 else  

   dbms_output.put_line('found null');  

 end if;  

 loop  

  fetch c_postype into v_postype,v_description ;  

  exit when c_postype%notfound;  

  dbms_output.put_line('postype:'||v_postype||',description:'||v_description);  

 end loop;  

 close c_postype;  

dbms_output.put_line('---loop end---');  

 open c_postype;  

   fetch c_postype into v_postype,v_description;  

   while c_postype%found loop  

     dbms_output.put_line('postype:'||v_postype||',description:'||v_description);  

     fetch c_postype into v_postype,v_description ;  

   end loop;  

 

 close c_postype;  

dbms_output.put_line('---while end---');  

 for v_pos in c_postype loop  

   v_postype := v_pos.pos_type;  

   v_description := v_pos.description;  

   dbms_output.put_line('postype:'||v_postype||',description:'||v_description);  

 end loop;  

 dbms_output.put_line('---for end---');  

end;  

选取游标从前必要开打游标,open cursor,循环完后再关闭游标close cursor.

那是行使游标应该慎记于心的原理。

地方的进度演示了游标循环的二种艺术。

在斟酌循环方法从前,大家先看看%found和%notfound这个游标的性质。

open c_postype;  

if c_postype%found then  

  dbms_output.put_line('found true');  

elsif c_postype%found = false then  

  dbms_output.put_line('found false');  

else  

  dbms_output.put_line('found null');  

end if;  

在开辟二个游标之后,立时检查它的%found或%notfound属性,它拿走的结果即不是true亦不是false.而是null.必得进行一条fetch语句后,那几个属性才有值。

先是种采用loop 循环

loop  

  fetch c_postype into v_postype,v_description ;  

  exit when c_postype%notfound;  

  ……  

end loop  

那边须求小心,exit when语句应当要紧跟在fetch之后。必防止多余的多少管理。

拍卖逻辑必要跟在exit when未来。这或多或少内需多加小心。

巡回甘休后要记得关闭游标。

其次种采用while循环。

  fetch c_postype into v_postype,v_description;  

while c_postype%found loop  

  ……  

     fetch c_postype into v_postype,v_description ;  

end loop;  

咱俩掌握了二个游标张开后,必得进行一回fetch语句,游标的习性才会起效果。所以利用while 循环时,就要求在循环从前举办壹回fetch动作。

再者数量处理动作必须放在循环体内的fetch方法以前。循环体内的fetch方法要放在最终。不然就能够多管理叁次。这或多或少也要那么些的小心。

总来说之,使用while来循环管理游标是最复杂的格局。

第三种 for循环

for v_pos in c_postype loop  

  v_postype := v_pos.pos_type;  

  v_description := v_pos.description;  

  …  

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这种特别,未有严重到要让程序中断的程度,能够完全交由由程序开展管理。

create or replace procedure procexception(p varchar2)  

as    

 v_postype varchar2(20);  

begin  

  select pos_type into v_postype from pos_type_tbl where 1=0;  

   dbms_output.put_line(v_postype);  

end;  

     

推行那一个历程

SQL> exec procexception('a');  

报错  

ORA-01403: no data found  

ORA-06512: at "LIFEMAN.PROCEXCEPTION", line 6  

ORA-06512: at line 1  

管理这一个有多个法子

1. 直接助长特别管理。

create or replace procedure procexception(p varchar2)  

as    

 v_postype varchar2(20);  

   

begin  

  select pos_type into v_postype from pos_type_tbl where 1=0;  

   dbms_output.put_line(v_postype);  

exception    

 when no_data_found then  

   dbms_output.put_line('没找到数据');  

end;  

这般做换汤不换药,程序照旧被暂停。只怕那样不是大家所想要的。

  1. select into做为一个独立的块,在那一个块中开展特别处理

create or replace procedure procexception(p varchar2)  

as    

 v_postype varchar2(20);  

   

begin  

 begin  

  select pos_type into v_postype from pos_type_tbl where 1=0;  

   dbms_output.put_line(v_postype);  

exception    

 when no_data_found then  

   v_postype := '';  

 end;  

 dbms_output.put_line(v_postype);  

end;  

那是一种比较好的管理方式了。不会因为那一个充足而引起程序中断。

3.应用游标

create or replace procedure procexception(p varchar2)  

as    

 v_postype varchar2(20);  

 cursor c_postype is select pos_type  from pos_type_tbl where 1=0;  

begin  

 open c_postype;  

   fetch c_postype into v_postype;  

 close c_postype;  

 dbms_output.put_line(v_postype);  

end;  

这样就全盘的防止了no_data_found至极。完全交由程序员来扩充支配了。

第三种景况是too_many_rows 非凡的难题。

Too_many_rows 这几个标题比起no_data_found要复杂一些。

给三个变量赋值时,然而查询结果有七个记录。

管理这种主题素材也是有三种情景:

1. 多条数据是基本上能用的,也正是说从结果聚集随意取三个值就行。这种状态相应很极端了吗,倘使出现这种情景,也验证了前后相继的严格性存在难点。

2. 多条数据是不可能被接受的,在这种情况确定是前后相继的逻辑出了难点,也说是说原本根本就不会想到它会生出多条记下。

对于第一种状态,就不可能不运用游标来处理,而对于第两种情景就亟须选取其中块来处理,重新抛出十三分。

多条数据足以承受,随意取一条,那几个跟no_data_found的管理方式同样,使用游标。

自己这里仅说第三种处境,不可承受多条数据,然而并不是忘了拍卖no_data_found哦。那就不能够利用游标了,必得使用个中块。

create or replace procedure procexception2(p varchar2)  

as    

 v_postype varchar2(20);  

   

begin  

 begin  

   select pos_type into v_postype from pos_type_tbl where rownum < 5;  

 exception  

   when no_data_found then  

     v_postype :=null;  

   when too_many_rows then  

     raise_application_error(-20000,'对v_postype赋值时,找到多条数据');  

 end;  

dbms_output.put_line(v_postype);  

end;  

内需专一的是束手待毙要拉长对no_data_found的拍卖,对出现多条记下的意况则继续抛出十二分,让上一层来拍卖。

总的说来对于select into的说话须求专心那二种情景了。要求妥帖管理啊。

3.5 在蕴藏进程中回到结果集

大家采纳存款和储蓄进度都以重回值都以十足的,一时大家必要从进度中回到一个集聚。即多条数据。那有三种缓和方案。相比简单的做法是写一时表,但是这种做法不灵敏。並且爱戴麻烦。大家可以动用嵌套表来完结.未有三个成团类型能够与java的jdbc类型匹配。那就是指标与关周详据库的抵抗吧。数据库的目的并不可见统统转变为编制程序语言的靶子,还必须采纳关全面据库的管理情势。

create or replace package procpkg is  

  type refcursor is ref cursor;  

  procedure procrefcursor(p varchar2, p_ref_postypeList  out refcursor);  

end procpkg;  

 

create or replace package body procpkg is  

 procedure procrefcursor(p varchar2, p_ref_postypeList out  refcursor)  

 is  

   v_posTypeList PosTypeTable;  

 begin  

   v_posTypeList :=PosTypeTable();--最早化嵌套表  

   v_posTypeList.extend;  

   v_posTypeList(1) := PosType('A001','客商资料退换');  

   v_posTypeList.extend;  

   v_posTypeList(2) := PosType('A002','团体资料改动');  

   v_posTypeList.extend;  

   v_posTypeList(3) := PosType('A003','受益人更换');  

   v_posTypeList.extend;  

   v_posTypeList(4) := PosType('A004','续期交费情势改动');  

   open p_ref_postypeList for  select * from table(cast (v_posTypeList as PosTypeTable));  

 end;  

end procpkg;  

在常德中定义了一个游标变量,并把它当作存款和储蓄进度的参数类型。

在积攒进度中定义了贰个嵌套表变量,对数据写进嵌套表中,然后把嵌套表进行类型调换为table,游标变量从那几个嵌套表中开展询问。外部程序调用那个游标。

进而这么些进程需求定义多少个类型。

create or replace type PosType as Object (  

 posType varchar2(20),  

 description varchar2(50)  

);  

create or replace type PosTypeTable is table of PosType;

亟待小心,那七个品种不可能定义在淄博中,必得独立定义,那样java层能力选择。

在表面通过pl/sql来调用那么些进程特别简单。

set serveroutput on;  

declare    

 type refcursor is ref cursor;  

 v_ref_postype refcursor;  

 v_postype varchar2(20);  

 v_desc varchar2(50);  

begin  

 procpkg.procrefcursor('a',v_ref_postype);  

 loop  

   fetch  v_ref_postype into v_postype,v_desc;  

   exit when v_ref_postype%notfound;  

   dbms_output.put_line('posType:'|| v_postype || ';description:' || v_desc);  

 end loop;  

end;  

只顾:对于游标变量,不可能运用for循环来管理。因为for循环会隐式的施行open动作。而经过open for来展开的游标%isopen是为true的。相当于暗中同意展开的。Open二个早就open的游标是不当的。所以不能应用for循环来管理游标变量。

咱俩最重要商量的是什么样通过jdbc调用来管理这些输出参数。

conn = this.getDataSource().getConnection();  

CallableStatement call = conn.prepareCall("{call procpkg.procrefcursor(?,?)}");  

call.setString(1, null);  

call.registerOutParameter(2, OracleTypes.CURSOR);  

call.execute();  

ResultSet rsResult = (ResultSet) call.getObject(2);  

while (rsResult.next()) {  

 String posType = rsResult.getString("posType");  

 String description = rsResult.getString("description");  

 ......  

}  

那正是jdbc的管理方法。

Ibatis管理办法:

1.参数配置

<parameterMap id="PosTypeMAP" class="java.util.Map">    

<parameter property="p" jdbcType="VARCHAR" javaType="java.lang.String" />    

<parameter property="p_ref_postypeList" jdbcType="ORACLECURSOR" javaType="java.sql.ResultSet" mode="OUT" typeHandler="com.palic.elis.pos.dayprocset.integration.dao.impl.CursorHandlerCallBack" />    

</parameterMap>  

 

2.调用经过  

 <procedure id ="procrefcursor" parameterMap ="PosTypeMAP">  

     {call procpkg.procrefcursor(?,?)}  

 </procedure>  

 

3.概念自个儿的微型计算机  

 public class CursorHandlerCallBack implements TypeHandler{  

   public Object getResult(CallableStatement cs, int index) throws SQLException {  

       ResultSet rs = (ResultSet)cs.getObject(index);  

       List result = new ArrayList();  

       while(rs.next()) {  

           String postype =rs.getString(1);  

           String description = rs.getString(2);  

           CodeTableItemDTO posTypeItem = new CodeTableItemDTO();  

           posTypeItem.setCode(postype);  

           posTypeItem.setDescription(description);  

           result.add(posTypeItem);  

       }  

       return result;  

   }  

 

 

 

  1. dao方法  

   public List procPostype() {  

       String p = "";  

       Map para = new HashMap();  

       para.put("p",p);  

       para.put("p_ref_postypeList",null);  

        this.getSqlMapClientTemplate().queryForList("pos_dayprocset.procrefcursor",  para);  

        return (List)para.get("p_ref_postypeList");  

   }  

其一跟jdbc的措施非常的相似.

咱俩使用的是ibatis的2.0版本,比较麻烦。

一经是选用2.2以上版本就非常轻巧的。

因为能够在parameterMap中定义八个resultMap.这样就没有须求要团结定义管理器了。

能够从解析2.0和2.0的dtd文件知道。

下面的三种艺术都以这几个的千头万绪,尽管仅仅是亟需重临五个结实集,那就完全能够应用函数来促成了。

create or replace package procpkg is  

  type refcursor is ref cursor;  

  procedure procrefcursor(p varchar2, p_ref_postypeList  out refcursor);  

  function procpostype(p varchar2) return PosTypeTable;    

end procpkg;  

 

create or replace package body procpkg is  

 procedure procrefcursor(p varchar2, p_ref_postypeList out  refcursor)  

 is  

   v_posTypeList PosTypeTable;  

 begin  

   v_posTypeList :=PosTypeTable();--开端化嵌套表  

   v_posTypeList.extend;  

   v_posTypeList(1) := PosType('A001','顾客资料改动');  

   v_posTypeList.extend;  

   v_posTypeList(2) := PosType('A002','团体资料退换');  

   v_posTypeList.extend;  

   v_posTypeList(3) := PosType('A003','受益人更改');  

   v_posTypeList.extend;  

   v_posTypeList(4) := PosType('A004','续期交费方式退换');  

   open p_ref_postypeList for  select * from table(cast (v_posTypeList as PosTypeTable));  

 end;  

 

 function procpostype(p varchar2) return PosTypeTable  

 as  

  v_posTypeList PosTypeTable;  

 begin  

     v_posTypeList :=PosTypeTable();--起首化嵌套表  

   v_posTypeList.extend;  

   v_posTypeList(1) := PosType('A001','顾客资料改动');  

   v_posTypeList.extend;  

   v_posTypeList(2) := PosType('A002','团体资料更改');  

   v_posTypeList.extend;  

   v_posTypeList(3) := PosType('A003','收益人改动');  

   v_posTypeList.extend;  

   v_posTypeList(4) := PosType('A004','续期交费情势更动');  

   return  v_posTypeList;  

 end;  

end procpkg;  

ibatis配置

<resultMap id="posTypeResultMap" class="com.palic.elis.pos.common.dto.CodeTableItemDTO">  

  <result property="code" column="posType"/>  

  <result property="description" column="description"/>  

</resultMap>  

 

 <select id="procPostype" resultMap="posTypeResultMap">  

   select * from table(cast (procpkg.procpostype(#value#) as PosTypeTable))  

 </select>  

Dao的写法跟日常查询同一

public List queryPostype() {  

 return this.getSqlMapClientTemplate().queryForList("pos_dayprocset.procPostype", null);  

}  

有几点须要在意,这里无法动用索引表,而是嵌套表。

除此以外正是把嵌套表强制调换为普通表。

本文由永利集团登录网址发布于计算机入门,转载请注明出处:Oracle存储过程使用技巧与经验总结

关键词:

  • 上一篇:没有了
  • 下一篇:没有了