存储过程常用技巧.docx
- 文档编号:8880166
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:22
- 大小:26.31KB
存储过程常用技巧.docx
《存储过程常用技巧.docx》由会员分享,可在线阅读,更多相关《存储过程常用技巧.docx(22页珍藏版)》请在冰豆网上搜索。
存储过程常用技巧
我们在进行pl/sql编程时打交道最多的就是存储过程了。
存储过程的结构是非常的简单的,我们在这里除了学习存储过程的基本结构外,还会学习编写存储过程时相关的一些实用的知识。
如:
游标的处理,异常的处理,集合的选择等等
1.存储过程结构
1.1第一个存储过程
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2(20);
begin
v_name:
='张三丰';
p_para3:
=v_name;
dbms_output.put_line('p_para3:
'||p_para3);
end;
上面就是一个最简单的存储过程。
一个存储过程大体分为这么几个部分:
创建语句:
createorreplaceprocedure存储过程名
如果没有orreplace语句,则仅仅是新建一个存储过程。
如果系统存在该存储过程,则会报错。
Createorreplaceprocedure如果系统中没有此存储过程就新建一个,如果系统中有此存储过程则把原来删除掉,重新创建一个存储过程。
存储过程名定义:
包括存储过程名和参数列表。
参数名和参数类型。
参数名不能重复,参数传递方式:
IN,OUT,INOUT
IN表示输入参数,按值传递方式。
OUT表示输出参数,可以理解为按引用传递方式。
可以作为存储过程的输出结果,供外部调用者使用。
INOUT即可作输入参数,也可作输出参数。
参数的数据类型只需要指明类型名即可,不需要指定宽度。
参数的宽度由外部调用者决定。
过程可以有参数,也可以没有参数
变量声明块:
紧跟着的as(is)关键字,可以理解为pl/sql的declare关键字,用于声明变量。
变量声明块用于声明该存储过程需要用到的变量,它的作用域为该存储过程。
另外这里声明的变量必须指定宽度。
遵循PL/SQL的变量声明规范。
过程语句块:
从begin关键字开始为过程的语句块。
存储过程的具体逻辑在这里来实现。
异常处理块:
关键字为exception,为处理语句产生的异常。
该部分为可选
结束块:
由end关键字结果。
1.2存储过程的参数传递方式
存储过程的参数传递有三种方式:
IN,OUT,INOUT.
IN按值传递,并且它不允许在存储过程中被重新赋值。
如果存储过程的参数没有指定存参数传递类型,默认为IN
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2(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:
Procedurecreatedwithcompilationerrors
SQL>showerror;
ErrorsforPROCEDURELIFEMAN.PROC1:
LINE/COLERROR
------------------------------------------------------------------------------
8/3PLS-00363:
expression'P_PARA1'cannotbeusedasanassignmenttarget
8/3PL/SQL:
Statementignored
这一点与其它高级语言都不同。
它相当于java在参数前面加上final关键字。
OUT参数:
作为输出参数,需要注意,当一个参数被指定为OUT类型时,就算在调用存储过程之前对该参数进行了赋值,在存储过程中该参数的值仍然是null.
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2(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>varp1varchar2(10);
SQL>varp2varchar2(10);
SQL>varp3varchar2(10);
SQL>exec:
p1:
='aaaa';
SQL>exec:
p2:
='bbbb';
SQL>exec:
p3:
='cccc';
SQL>execproc1(:
p1,:
p2,:
p3);
p_para1:
aaaa
p_para2:
p_para3:
张三丰
SQL>execdbms_output.put_line(:
p2);
PL/SQLproceduresuccessfullycompleted
p2
---------
INOUT是真正的按引用传递参数。
即可作为传入参数也可以作为传出参数。
1.3存储过程参数宽度
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2
(2);
begin
v_name:
=p_para1;
end;
SQL>varp1varchar2(10);
SQL>varp2varchar2(20);
SQL>varp3varchar2(30);
SQL>exec:
p1:
='aaaaaa';
SQL>execproc1(:
p1,:
p2,:
p3);
ORA-06502:
PL/SQL:
numericorvalueerror:
characterstringbuffertoosmall
ORA-06512:
at"LIFEMAN.PROC1",line8
ORA-06512:
atline1
首先,我们要明白,我们无法在存储过程的定义中指定存储参数的宽度,也就导致了我们无法在存储过程中控制传入变量的宽度。
这个宽度是完全由外部传入时决定的。
我们再来看看OUT类型的参数的宽度。
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2
(2);
begin
p_para2:
='aaaaaaaaaaaaaaaaaaaa';
end;
SQL>varp1varchar2
(1);
SQL>varp2varchar2
(1);
SQL>varp3varchar2
(1);
SQL>exec:
p2:
='a';
SQL>execproc1(:
p1,:
p2,:
p3);
在该过程中,p_para2被赋予了20个字符a.
而在外部的调用过程中,p2这个参数仅仅被定义为varchar2
(1).
而把p2作为参数调用这个过程,却并没有报错。
而且它的真实值就是20个a
SQL>selectdump(:
p2)fromdual;
DUMP(:
P2)
---------------------------------------------------------------------------
Typ=1Len=20:
97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97,97
p2
---------
aaaaaaaaaaaaaaaaaaaa
再来看看INOUT参数的宽度
createorreplaceprocedureproc1(
p_para1varchar2,
p_para2outvarchar2,
p_para3inoutvarchar2
)as
v_namevarchar2
(2);
begin
p_para3:
='aaaaaaaaaaaaaaaaaaaa';
end;
SQL>varp1varchar2
(1);
SQL>varp2varchar2
(1);
SQL>varp3varchar2
(1);
SQL>execproc1(:
p1,:
p2,:
p3);
执行这个过程,仍然正确执行。
可见,对于IN参数,其宽度是由外部决定。
对于OUT和INOUT参数,其宽度是由存储过程内部决定。
因此,在写存储过程时,对参数的宽度进行说明是非常有必要的,最明智的方法就是参数的数据类型使用%type。
这样双方就达成了一致。
1.3参数的默认值
存储过程的参数可以设置默认值
createorreplaceprocedureprocdefault(p1varchar2,
p2varchar2default'mark')
as
begin
dbms_output.put_line(p2);
end;
SQL>setserveroutputon;
SQL>execprocdefault('a');
mark
可以通过default关键字为存储过程的参数指定默认值。
在对存储过程调用时,就可以省略默认值。
需要注意的是:
默认值仅仅支持IN传输类型的参数。
OUT和INOUT不能指定默认值
对于有默认值的参数不是排在最后的情况。
createorreplaceprocedureprocdefault2(
p1varchar2default'remark',
p2varchar2)
as
begin
dbms_output.put_line(p1);
end;
第一个参数有默认值,第二个参数没有。
如果我们想使用第一个参数的默认值时
execprocdefault2('aa');
这样是会报错的。
那怎么变呢?
可以指定参数的值。
SQL>execprocdefault2(p2=>'aa');
remark
这样就OK了,指定aa传给参数p2
2.存储过程内部块
2.1内部块
我们知道了存储过程的结构,语句块由begin开始,以end结束。
这些块是可以嵌套。
在语句块中可以嵌套任何以下的块。
Declare…begin…exception…end;
createorreplaceprocedureinnerBlock(p1varchar2)
as
o1varchar2(10):
='out1';
begin
dbms_output.put_line(o1);
declare
inner1varchar2(20);
begin
inner1:
='inner1';
dbms_output.put_line(inner1);
declare
inner2varchar2(20);
begin
inner2:
='inner2';
dbms_output.put_line(inner2);
end;
exception
whenothersthen
null;
end;
end;
需要注意变量的作用域。
3.存储过程的常用技巧
3.1哪种集合?
我们在使用存储过程的时候经常需要处理记录集,也就是多条数据记录。
分为单列多行和多列多行,这些类型都可以称为集合类型。
我们在这里进行比较这些集合类型,以便于在编程时做出正确的选择。
索引表,也称为pl/sql表,不能存储于数据库中,元素的个数没有限制,下标可以为负值。
typet_tableistableofvarchar2(20)indexbybinary_integer;
v_studentt_table;
varchar2(20)表示存放元素的数据类型,binary_integer表示元素下标的数据类型。
嵌套表,索引表没有indexby子句就是嵌套表,它可以存放于数据中,元素个数无限,下标从1开始,并且需要初始化
typet_nestTableistableofvarchar2(20);
v_classt_nestTable;
仅是这样声明是不能使用的,必须对嵌套表进行初始化,对嵌套表进行初始化可以使用它的构造函数
v_class:
=t_nestTable('a','b','c');
变长数组,变长数组与高级语言的数组类型非常相似,下标以1开始,元素个数有限。
typet_arrayisvarray(20)ofvarchar2(20);
varray(20)就定义了变长数组的最大元素个数是20个
变长数组与嵌套表一样,也可以是数据表列的数据类型。
同时,变长数组的使用也需要事先初始化。
类型可存储于数据库元素个数是否需初始化初始下标值
索引表否无限不需
嵌套表可无限需1
可变数组可有限(自定义)需1
由此可见,如果仅仅是在存储过程中当作集合变量使用,索引表是最好的选择。
3.2选用何种游标?
显示游标分为:
普通游标,参数化游标和游标变量三种。
下面以一个过程来进行说明
createorreplaceprocedureproccursor(pvarchar2)
as
v_rownumnumber(10):
=1;
cursorc_postypeisselectpos_typefrompos_type_tblwhererownum=1;
cursorc_postype1isselectpos_typefrompos_type_tblwhererownum=v_rownum;
cursorc_postype2(p_rownumnumber)isselectpos_typefrompos_type_tblwhererownum=p_rownum;
typet_postypeisrefcursor;
c_postype3t_postype;
v_postypevarchar2(20);
begin
openc_postype;
fetchc_postypeintov_postype;
dbms_output.put_line(v_postype);
closec_postype;
openc_postype1;
fetchc_postype1intov_postype;
dbms_output.put_line(v_postype);
closec_postype1;
openc_postype2
(1);
fetchc_postype2intov_postype;
dbms_output.put_line(v_postype);
closec_postype2;
openc_postype3forselectpos_typefrompos_type_tblwhererownum=1;
fetchc_postype3intov_postype;
dbms_output.put_line(v_postype);
closec_postype3;
end;
cursorc_postypeisselectpos_typefrompos_type_tblwhererownum=1
这一句是定义了一个最普通的游标,把整个查询已经写死,调用时不可以作任何改变。
cursorc_postype1isselectpos_typefrompos_type_tblwhererownum=v_rownum;
这一句并没有写死,查询参数由变量v_rownum来决定。
需要注意的是v_rownum必须在这个游标定义之前声明。
cursorc_postype2(p_rownumnumber)isselectpos_typefrompos_type_tblwhererownum=p_rownum;
这一条语句与第二条作用相似,都是可以为游标实现动态的查询。
但是它进一步的缩小了参数的作用域范围。
但是可读性降低了不少。
typet_postypeisrefcursor;
c_postype3t_postype;
先定义了一个引用游标类型,然后再声明了一个游标变量。
openc_postype3forselectpos_typefrompos_type_tblwhererownum=1;
然后再用openfor来打开一个查询。
需要注意的是它可以多次使用,用来打开不同的查询。
从动态性来说,游标变量是最好用的,但是阅读性也是最差的。
注意,游标的定义只能用使关键字IS,它与AS不通用。
3.3游标循环最佳策略
我们在进行PL/SQL编程时,经常需要循环读取结果集的数据。
进行逐行处理,这个过程就需要对游标进行循环。
对游标进行循环的方法有多种,我们在此一一分析。
createorreplaceprocedureproccycle(pvarchar2)
as
cursorc_postypeisselectpos_type,descriptionfrompos_type_tblwhererownum<6;
v_postypevarchar2(20);
v_descriptionvarchar2(50);
begin
openc_postype;
ifc_postype%foundthen
dbms_output.put_line('foundtrue');
elsifc_postype%found=falsethen
dbms_output.put_line('foundfalse');
else
dbms_output.put_line('foundnull');
endif;
loop
fetchc_postypeintov_postype,v_description;
exitwhenc_postype%notfound;
dbms_output.put_line('postype:
'||v_postype||',description:
'||v_description);
endloop;
closec_postype;
dbms_output.put_line('---loopend---');
openc_postype;
fetchc_postypeintov_postype,v_description;
whilec_postype%foundloop
dbms_output.put_line('postype:
'||v_postype||',description:
'||v_description);
fetchc_postypeintov_postype,v_description;
endloop;
closec_postype;
dbms_output.put_line('---whileend---');
forv_posinc_postypeloop
v_postype:
=v_pos.pos_type;
v_description:
=v_pos.description;
dbms_output.put_line('postype:
'||v_postype||',description:
'||v_description);
endloop;
dbms_output.put_line('---forend---');
end;
使用游标之前需要开打游标,opencursor,循环完后再关闭游标closecursor.
这是使用游标应该慎记于心的法则。
上面的过程演示了游标循环的三种方法。
在讨论循环方法之前,我们先看看%found和%notfound这些游标的属性。
openc_postype;
ifc_postype%foundthen
dbms_output.put_line('foundtrue');
elsifc_postype%found=falsethen
dbms_output.put_line('foundfalse');
else
dbms_output.put_line('foundnull');
endif;
在打开一个游标之后,马上检查它的%found或%notfound属性,它得到的结果即不是true也不是false.而是null.必须执行一条fetch语句后,这些属性才有值。
第一种使用loop循环
loop
fetchc_postypeintov_postype,v_description;
exitwhenc_postype%notfound;
……
endloop
这里需要注意,exitwhen语句一定要紧跟在fetch之后。
必避免多余的数据处理。
处理逻辑需要跟在exitwhen之后。
这一点需要多加小心。
循环结束后要记得关闭游标。
第二种使用while循环。
fetchc_postypeintov_postype,v_description;
whilec_postype%foundloop
……
fetchc_postypeintov_postype,v_descriptio
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 存储 过程 常用 技巧
![提示](https://static.bdocx.com/images/bang_tan.gif)