8. 存储子程序(命名块)
存储子程序下面三种:
(1) 存储过程:store procedure(SP)
(2) 函数:function(FUN)
(3) 包和包体:package/package body(PKG)
存储过程:oracle的一种对象
v_sqlcode
A 在一个用户下有唯一的名字
B 存储在数据库中
C 可以接收传入参数并且有多个返回值
D 可以直接执行和在其他程序中调用
E 不可以被select语句调用
函数:oracle的一种对象
A 在一个用户下有唯一的名字
B 存储在数据库中
C 可以接收传入参数并且必须函数整体有一个返回值
D 可以直接执行和在其他程序中调用
E 可以被select语句调用,max(),min()这些就是oracle定义好的函数
##########################################################################################
8.1 存储过程的简单案例
(1) 创建一个目标表
create table tt(actual_line varchar2(150));
(2) 创建存储过程
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200); --定义全局变量;
begin
for idx in 1..ip_line_length loop
actual_line:=actual_line||ip_separator;
end loop;
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
select * from user_objects where object_type=‘PROCEDURE‘; --存储在数据库中
查看存储过程的创建的代码
SQL> set long 10000;
SQL> select dbms_metadata.get_ddl(‘PROCEDURE‘,‘SHOW_LINE‘) from dual;
DBMS_METADATA.GET_DDL(‘PROCEDURE‘,‘SHOW_LINE‘)
--------------------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE "PLSQL"."SHOW_LINE" (ip_line_length in number,ip_s
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
for idx in 1..ip_line_length loop
actual_line:=actual_line||ip_separator;
end loop;
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show
line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
(3)调用存储过程
SQL> exec show_line(5,‘abc‘);
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
SQL> exec show_line(100,‘abc‘);
PL/SQL procedure successfully completed. --一定会有异常,但是程序不会中断,因为有异常处理部分
############################################################################################8.2 函数的简单案例
函数一般是不用来操作数据库对象或者表,就是作一种运算
需要定义返回值的数据类型,函数是必须有一个返回值
create or replace function f_line(ip_line_length in number,ip_separator in varchar2) return varchar2 is
actual_line varchar2(150); --定义函数的全局变量
begin
for idx in 1..ip_line_length loop
actual_line:=actual_line||ip_separator;
end loop;
return actual_line;
exception when others then
return ‘ERROR‘;
end;
测试:
SQL> select f_line(4,‘@‘) from dual;
F_LINE(4,‘@‘)
--------------------------------------------------------------------------------
@@@@
SQL> select f_line(5,‘#‘) from dual;
F_LINE(5,‘#‘)
--------------------------------------------------------------------------------
#####
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
SQL> exec show_line(10,‘a‘);
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
aaaaaaaaaa
函数return返回值有三种方式:
A 返回常量:return ‘ERROR!‘;
B 返回变量:return actual_line;
C 返回表达式:return actual_line||‘AAA‘;
return有两种表现形式:
A 在FUN里面,终止程序,返回值
B 在SP中可以写return,强行终止程序,return后面什么都不写,不会返回值
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
return;
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
创建SP和FUN本身是DDL语句,对应在数据库中有对象存在,可以查看他们定义的语句
select * from user_source where name=‘F_LINE‘;
###########################################################################################
8.3 函数和存储过程的调用
(1) 在匿名块中调用
declare
var1 varchar2(50);
begin
show_line(3,‘####‘); --存储过程的调用,直接调用
var1:=f_line(3,‘****‘); --函数的调用,需要赋给一个变量
dbms_output.put_line(var1);
end;
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
aaaaaaaaaa
############
(2) execute命令调用
A SP调用
SQL> exec show_line(6,‘sdfa‘);
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
aaaaaaaaaa
sdfasdfasdfasdfasdfasdfa
############
SQL> execute show_line(6,‘1231‘);
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
aaaaaaaaaa
sdfasdfasdfasdfasdfasdfa
123112311231123112311231
############
B FUN调用
SQL> variable var1 varchar2(150); --需要先定义一个临时的变量
SQL> exec :var1:=f_line(20,‘%‘); --在变量的前面注意有冒号的存在
PL/SQL procedure successfully completed.
SQL> print var1;
VAR1
--------------------------------------------------------------------------------
%%%%%%%%%%%%%%%%%%%%
(3) call命令,常见于java程序中
A SP调用
SQL> call show_line(4,‘OK‘);
Call completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
abcabcabcabcabc
aaaaaaaaaa
sdfasdfasdfasdfasdfasdfa
123112311231123112311231
OKOKOKOK
############
6 rows selected.
B FUN调用
SQL> variable var2 varchar2(150); --定义临时变量来接收
SQL> call f_line(20,‘$‘) into :var2; --函数的返回值需要使用into赋给变量,注意变量前面有冒号
Call completed.
SQL> print var2;
VAR2
--------------------------------------------------------------------------------
$$$$$$$$$$$$$$$$$$$$
(4)在命名块中
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
##########################################################################################
8.4 存储过程参数的使用
(1) 命名参数
参数:形式参数 , 实际参数
A 形式参数:show_line(ip_line_length in number,ip_separator in varchar2) --形参列表,形参不需要预先定义的
B 实际参数:show_line(4,‘OK‘);
(2) 命名参数的传入值的方法
A 位置表示法:按照形参摆放的位置赋给参数
declare
v_length number:=50;
v_separator varchar2(1):=‘^‘;
begin
show_line(v_length,v_separator);
end;
B 名称表示法
declare
v_length number:=50;
v_separator varchar2(1):=‘^‘;
begin
show_line(ip_separator => v_separator,ip_line_length => v_length); --手动指定变量的对应关系
end;
C 混合表示法
declare
v_length number:=50;
v_separator varchar2(1):=‘^‘;
begin
show_line(32,ip_separator => v_separator);
end;
(3) 参数的默认值
create or replace procedure show_line(ip_line_length in number default 20,ip_separator in varchar2 default ‘*‘) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
第一种情况,两个参数都不赋值
SQL> exec show_line;
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
********************
第二种情况:
A 只给第一个参数赋值
SQL> exec show_line(13); --默认使用位置表示法
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
********************
*************
B 只给第二个参数赋值
SQL> exec show_line(‘@@‘); --默认使用位置表示法,报错
BEGIN show_line(‘@@‘); END;
*
ERROR at line 1:
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at line 1
SQL> exec show_line(ip_separator => ‘@@‘); --此时必须使用名称表示法
PL/SQL procedure successfully completed.
SQL> select * from tt;
ACTUAL_LINE
--------------------------------------------------------------------------------
********************
*************
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(4) PLSQL参数的模式
IN 模式:只读模式,被调用的形参值在SP或者FUN中不能改变,其实就是传入参数,IN可以省略不写的
OUT 模式:写模式参数,允许在SP或者FUN中给其赋值,可以写入,然后作为返回值返回程序主体中。
IN OUT 模式:结合上面两种,一种读写的模式,允许从实参中将其读出,也可以将这个参数赋值然后作为返回值到程序主体中但是不能有缺省值
IN模式参数不能被赋值:
create or replace procedure show_line(ip_line_length in number default 20,ip_separator in varchar2 default ‘*‘) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator);
ip_line_length:=120; --给IN模式参数赋值报错 PLS,IN参数应该是调用SP时带进来的
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
将参数的模式改为IN OUT模式,同时去掉默认值,编译通过
create or replace procedure show_line(ip_line_length in out number,ip_separator in varchar2 default ‘*‘) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator);
ip_line_length:=120; --没有问题,编译通过,IN OUT模式
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
给上面的程序添加一个OUT参数
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2 default ‘*‘,op_line out varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
op_line:=actual_line; --给OUT参数赋值,没有任何错误
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
如何使用OUT参数:
declare
v_op_line varchar2(150);
begin
show_line(50,‘R‘,v_op_line); --必须定义一个变量来接受OUT参数
dbms_output.put_line(v_op_line);
end;
OUT参数有多个--存储过程有多个返回值
create or replace procedure show_line(ip_line_length in number,ip_separator in varchar2 default ‘*‘,op_line out varchar2,op_x out varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
op_line:=actual_line;
op_x:=to_char(ip_line_length);
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
declare
v_op_line varchar2(150);
v_op_x varchar2(20);
begin
show_line(50,‘R‘,v_op_line,v_op_x);
dbms_output.put_line(v_op_line);
dbms_output.put_line(v_op_x);
end;
有OUT参数的存储过程不能够直接调用
declare
v_op_line varchar2(150);
v_op_x varchar2(20);
begin
show_line(50,‘R‘); --必须要先定义接受返回值的变量
end;
IN OUT模式参数的使用
create or replace procedure show_line1(ip_line_length in out number,ip_separator in varchar2 default ‘*‘,op_line out varchar2) is
actual_line varchar2(150);
v_sqlcode number;
v_sqlerrm varchar2(200);
begin
actual_line:=f_line(ip_line_length,ip_separator); --在命名块中调用命名块
op_line:=actual_line; --给OUT参数赋值,没有任何错误
ip_line_length:=123;
insert into tt values(actual_line);
commit;
exception when others then
v_sqlcode:=sqlcode;
v_sqlerrm:=sqlerrm;
insert into exception_monitor values(‘TT‘,substr(actual_line,1,50),upper(‘show_line‘),‘OTHERS‘,v_sqlcode,v_sqlerrm,sysdate);
commit;
end;
declare
v_op_line varchar2(150);
v_ip_op number:=50;
begin
show_line1(v_ip_op,‘R‘,v_op_line); --必须定义一个变量给IN OUT模式的参数,直接传入一个值是不行的
dbms_output.put_line(v_op_line);
dbms_output.put_line(v_ip_op);
end;
(5) 函数和存储过程返回参数的区别:
A 形式:函数使用return返回的,SP使用OUT参数返回的。
B 个数:FUN只能返回一个值,SP可以定义多个OUT参数返回多个值
C 函数的返回值可以用来给变量赋值:actual_line:=f_line(ip_line_length,ip_separator);
SP的返回值必须用变量在形式参数列表的位置接受值。
declare
v_op_line varchar2(150);
v_op_x varchar2(20);
begin
show_line(50,‘R‘,v_op_line,v_op_x);
dbms_output.put_line(v_op_line);
dbms_output.put_line(v_op_x);
end;
##########################################################################################
8.5 参数的类型
(1) 可以将一个subtype子类型定义为主类型的子类型,可以讲存储子程序的定义,放在declare,不存储,临时调用
declare
subtype st1 is number(16,3) not null;
v_value st1 default 0;
procedure p1(ip_1 st1) is
begin
dbms_output.put_line(to_char(ip_1));
end;
begin
v_value:=12;
p1(v_value);
end;
declare
v_value number;
procedure p1(ip_1 number) is
begin
dbms_output.put_line(to_char(ip_1));
end;
begin
v_value:=12;
p1(v_value);
end;
(2) 参数可以是记录、数组、嵌套表、index-by表
案例1:给SP传入参数是记录类型
declare
type hrc_org_rec is record(hrc_org_id number,hrc_descr varchar2(20),org_short_name varchar2(50));
v_example_rec_x hrc_org_rec;
cursor cur_org is select hrc_org_seq.nextval,h.hrc_descr,o.org_short_name
from org_tab o,hrc_tab h
where o.hrc_code=h.hrc_code;
procedure test_record(v_example_rec_y in hrc_org_rec) is --参数是记录类型
begin
dbms_output.put_line(rpad(to_char(v_example_rec_y.hrc_org_id),10,‘ ‘)||‘ ‘||rpad(v_example_rec_y.hrc_descr,20,‘ ‘)||‘ ‘||rpad(v_example_rec_y.org_short_name,30,‘ ‘));
exception when others then
null;
end;
begin
open cur_org;
loop
fetch cur_org into v_example_rec_x;
exit when(cur_org%notfound);
test_record(v_example_rec_x);
end loop;
close cur_org;
exception when others then
null;
end;
案例2:给SP传入的参数是游标
declare
v_rc1 Sys_Refcursor; --定义一个系统已经定义好的游标类型的游标变量
procedure test_cursor(v_rc in Sys_Refcursor) is
type hrc_org_rec is record(hrc_org_id number,hrc_descr varchar2(20),org_short_name varchar2(50));
v_example_rec_y hrc_org_rec;
begin
loop
fetch v_rc into v_example_rec_y ;
exit when(v_rc%notfound);
dbms_output.put_line(rpad(to_char(v_example_rec_y.hrc_org_id),10,‘ ‘)||‘ ‘||rpad(v_example_rec_y.hrc_descr,20,‘ ‘)||‘ ‘||rpad(v_example_rec_y.org_short_name,30,‘ ‘));
end loop;
if v_rc%isopen then
close v_rc;
end if;
exception when others then
null;会报
end;
begin
open v_rc1 for select hrc_org_seq.nextval,h.hrc_descr,o.org_short_name
from org_tab o,hrc_tab h
where o.hrc_code=h.hrc_code;
test_cursor(v_rc1); --传入的游标变量需要是已经是打开状态的。
if v_rc1%isopen then
close v_rc1;
end if;
exception when others then
null;
end;
练习:给SP传入参数是数组
SCOTT用户下,dept,根据传入的部门号,取到所有的员工号,存储在数组里,把这个数组传入一个SP,在这个SP中,将这个数组的内容一次输出。
要求:
A SP1:传入deptno OUT参数返回数组(员工号)
B SP2:传入数组,输出数组内容,没有OUT参数
C 封装起来调用。
declare
v_numvarray add_list:=add_list(null); --这里的参数是接受返回值用的,也可以不用初始化
procedure sp1(v_deptno in number,op_varray out add_list) is
i integer:=1;
begin
op_varray:=add_list(null); --这里需要对形参的数组变量进行初始化,否则用extend方法会报ORA-00900的错
op_varray.delete; --这里将第一个元素删除后,数组还是初始化的状态,此时可以使用extend方法
for idx in (select a.empno
from emp a
where a.deptno=v_deptno) loop
op_varray.extend;
op_varray(i):=idx.empno;
i:=i+1;
end loop;
end;
procedure sp2(v_numvarray in add_list) is
begin
for i in 1..v_numvarray.count loop
dbms_output.put_line(v_numvarray(i));
end loop;
end;
begin
sp1(20,v_numvarray);
sp2(v_numvarray);
end;
#########################################################################################
8.6 函数返回结果集的情况
(1)函数得到的游标使用完之后,游标是打开的还是关闭的?
答:打开的,所以要在程序结尾关掉。
(2)函数能不能有OUT参数??
答:能
create or replace function getcursor return sys_refcursor is
v_rc sys_refcursor;
begin
open v_rc for select * from hrc_tab;
return v_rc;
exception when others then
return null;
end;
declare
v_rc sys_refcursor;
hrc_rec hrc_tab%rowtype;
begin
v_rc:=getcursor;
loop
fetch v_rc into hrc_rec;
exit when(v_rc%notfound);
dbms_output.put_line(to_char(hrc_rec.hrc_code)||‘ ‘||hrc_rec.hrc_descr);
end loop;
end;
v_rc使用完之后,游标是打开的还是关闭的? 打开的
declare
v_rc sys_refcursor;
hrc_rec hrc_tab%rowtype;
begin
v_rc:=getcursor;
loop
fetch v_rc into hrc_rec;
exit when(v_rc%notfound);
dbms_output.put_line(to_char(hrc_rec.hrc_code)||‘ ‘||hrc_rec.hrc_descr);
end loop;
if v_rc%isopen then
dbms_output.put_line(‘YES‘); --实验输出为YES,游标是打开
else
dbms_output.put_line(‘NO‘);
end if;
end;
需要在结尾处关闭掉游标
declare
v_rc sys_refcursor;
hrc_rec hrc_tab%rowtype;
begin
v_rc:=getcursor;
loop
fetch v_rc into hrc_rec;
exit when(v_rc%notfound);
dbms_output.put_line(to_char(hrc_rec.hrc_code)||‘ ‘||hrc_rec.hrc_descr);
end loop;
if v_rc%isopen then
close v_rc;
end if;
end;
(3) 函数能不能有OUT参数??
declare
v_rc sys_refcursor;
v_rc1 sys_refcursor;
hrc_rec hrc_tab%rowtype;
begin
v_rc:=getcursor(v_rc1);
loop
fetch v_rc1 into hrc_rec;
exit when(v_rc1%notfound);
dbms_output.put_line(to_char(hrc_rec.hrc_code)||‘ ‘||hrc_rec.hrc_descr);
end loop;
if v_rc%isopen then
dbms_output.put_line(‘YES‘);
close v_rc;
else
dbms_output.put_line(‘NO‘);
end if;
if v_rc1%isopen then
dbms_output.put_line(‘YES‘);
close v_rc1;
else
dbms_output.put_line(‘NO‘);
end if;
end;
函数也是可以有OUT参数的