oracle 存储过程及REF CURSOR的使用

时间:2021-05-31 18:32:19

摘要:主要记录了oracle的stored procedure相关知识、从定义到使用。从最简单的例子入手、逐渐深入。同时后面结合了ref cursor的使用。顺便补充了在java中如何使用。
一:简介

1、存储过程定义:
存储过程(Stored Procedure )是一组为了完成特定功能的SQL 语句集,经编译后存储在数据库中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。

存储过程是由流控制的和SQL 语句书写的过程,这个过程经编译和优化后存储在数据库服务器中,应用程序使用时只要调用即可。在ORACLE 中,若干个有联系的过程可以组合在一起构成程序包。

2、优点:
a) 存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。

b) 当对数据库进行复杂操作时(如对多个表进行Update、Insert、Query、Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务管理结合一起使用。

c)存储过程可以重复使用,可减少数据库开发人员的工作量。

d)安全性高,可设定只有某用户才具有对指定存储过程的使用权。

简单说,你在你的机器上写了个存储过程,这个存储过程像那些表里的数据一样被放在遥远的数据库服务器当中,但是它又是可执行的代码,其他能连到数据库服务器的用户,可以调用你写的存储过程
它的作用是隐藏细节,就是说,你写的存储过程代码可能很复杂,但是其他人调用它却很简单,不用具体知道它是如何做的,且一次能完成多个指令

3、分类:

oracle有系统存储过程和自定义存储过程2种存储过程。

系统存储过程就是由oracle预先提供的一组完成特定功能的存储过程,安装完oracle就有了。

自定义存储过程就是存在oracle数据库里由一组plsql语句组成的自定义过程(procedure)。它可以供其它oracle自定义存储过程、自定义函数和job调用或者由客户端程序调用。

二:基本使用方法及示例

1、基本结构:

CREATEORREPLACE PROCEDURE 存储过程名字
(参数1INNUMBER,参数2INNUMBER
AS
变量1INTEGER:
=0;
变量2DATE;
BEGIN
END存储过程名字

2、 无参形式的procedure:

--无参procedure
createorreplace procedure pro_no_param
is
begin
dbms_output.put_line('the procedure without params');

endpro_no_param;

--调用
--one: 无参的procedure名字后面必须要()
call pro_no_param();


--two:procedure名称后面可以没有();
begin
pro_no_param();
end;

3、 参数类型为IN的procedure:

--有参procedure 只有IN类型
createorreplace procedure pro_in_param(
v_1innumber,
v_2invarchar2,
v_3indate
)

is
begin
dbms_output.put_line('v1: ' || v_1 || ' v2: ' || v_2 || ' v2: '|| (to_char(v_3, 'yyyy-mm-dd')));

endpro_in_param;

begin
pro_in_param(1,'chy', sysdate);
end;

4、 参数类型为OUT的procedure:

--有参procedure 只有OUT类型
createorreplace procedure pro_out_param(
v1outnumber,
v2outchar
)

is
begin
v1 :
= 2;
v2 := 'andyChen';
endpro_out_param;


--记得声明用于存放procedureout值的变量
--语句结束了一定记得结尾的 —— ;

declare
v_1 number;
v_2 varchar2(200);
begin
pro_out_param(v_1, v_2);
dbms_output.put_line('v1: ' || v_1 || ' v2: ' || v_2);
end;

5、 参数类型同时为IN和OUT的procedure:
–同时为INOUT参数的procedure
–用同一变量接收传入的值然后将这个变量当作输出的值赋给执行时声明的变量

createorreplace procedure pro_in_out_param(
in_out_paraminoutvarchar2
)

is
begin
in_out_param :
= 'in_out_param and ' || in_out_param;
endpro_in_out_param;

declare
in_out_param varchar2(222) := 'detail param';
begin
pro_in_out_param(in_out_param);
dbms_output.put_line(in_out_param);
end;

三:实例


CREATETABLEuser_info
(
id VARCHAR2(4) notnullprimary key,
nameVARCHAR2(15),
pwd VARCHAR2(15),
address VARCHAR2(30)
);

--创建一个添加用户的stored_procedure;
createorreplace procedure pro_addUser(
n_id user_info.id%type,
n_name user_info.name%type,
n_pwd user_info.pwd%TYPE,
n_address user_info.address%TYPE
)

as
begin
--插入数据
insertintouser_info(id,name,pwd,address)
values(n_id, n_name, n_pwd, n_address);

endpro_addUser;

--调用、有变量需要声明的时候才有declare、没有就直接begin
begin
pro_addUser('1','chy','admin','nanjin');
if SQL%found then
dbms_output.put_line('add successed');
endif;
end;

--根据id查询用户名和密码
createorreplace procedure pro_getUserInfo(
n_id user_info.id%type,
n_nameoutuser_info.name%type,
n_pwd outuser_info.pwd%type
)

as
begin
selectuser_info.name, user_info.pwd inton_name, n_pwd
fromuser_info
whereuser_info.id=n_id;

endpro_getUserInfo;


--调用
declare
v_id user_info.id%type := '1';
v_name user_info.name%type;
v_pwd user_info.pwd%type;
begin
pro_getUserInfo(v_id, v_name, v_pwd);
dbms_output.put_line('name: ' || v_name || ' pwd: ' || v_pwd);
end;

-- 打印九九乘法表
createorreplace procedure pro_multiplication_table
is
i integer;

j integer;
begin
foriin1..9 loop
forjin1..9 loop
if i>=j then
DBMS_output.put(To_Char(j)||'*'||to_char(i)||'='||to_char(i*j)||'   ');
endif;
endloop;
DBMS_output.put_line('');
endloop;
end pro_multiplication_table;

--调用
call pro_multiplication_table();

--使用自定义游标、根据工作and薪水查询员工姓名
createorreplace procedure pro_getName(
n_sal emp.sal%type,
n_ename outemp.ename%type,
n_jobinoutemp.job%type
)

is
n_count number;

cursorcurisselectename fromempwhereemp.sal > n_sal andemp.job=n_job;
n_row cur%rowtype;
begin
selectcount(*)inton_countfromempwhereemp.sal > n_sal andemp.job=n_job;
if n_count > 1 then
forn_rowincur loop
DBMS_output.put_line('职工姓名为:'||n_row.ename||' 工作为:'||n_job);
endloop;
else
DBMS_output.put_line('未查到符合条件的记录!');
endif;
end pro_getName;

-- 调用
declare
v_sal emp.sal%type := 2000;
v_job emp.job%type :='MANAGER';
v_ename emp.ename%type;
begin
pro_getName(v_sal, v_ename, v_job);
end;

--ref cursor的使用
--创建存放弱引用和强引用的cursor的包
createorreplace package refcursor_pkg
as
type weak_ref_cursor isrefcursor;
type strong_ref_cursor isrefcursorreturnemp%rowtype;
endrefcursor_pkg;

--将弱引用的cursor作为结果返回
createorreplace procedure test(
p_deptnoinnumber,
p_cursoroutrefcursor_pkg.weak_ref_cursor
)
isbegin
openp_cursorforselect* fromempwheredeptno=p_deptno;
endtest;

/**或者不用包直接使用下面这种定义
createorreplace procedure test_1(
p_deptnoINnumber,
p_cursorOUTSYS_REFCURSOR
)
is
begin
openp_cursorFORselect*fromempwhere deptno = p_deptno;
endtest_1;
*/

declare
v_deptno number := 20;
v_cursor refcursor_pkg.weak_ref_cursor;
r_emp emp%rowtype;
begin
test(v_deptno, v_cursor);
loop
fetchv_cursorintor_emp;
exitwhenv_cursor%notfound;
dbms_output.put_line('empno: ' || r_emp.empno || ' ename: ' || r_emp.ename || ' job: ' || r_emp.job);
endloop;
closev_cursor;
end;
//java中使用refcursor

publicvoid method() throws SQLException{
Connectionconn = getConnection();
CallableStatement cstmt = null;
ResultSet rs = null;
intdeptno = 10;
Objecttemp;
try{
cstmt = conn.prepareCall("begin test(?,?); end;");
cstmt.setInt(1, deptno);
cstmt.registerOutParameter(2, OracleTypes.CURSOR);
cstmt.execute();
rs = (ResultSet) cstmt.getObject(2);
ResultSetMetaData rsm = rs.getMetaData();
intcolumnCount = rsm.getColumnCount();
while (rs.next()){
for(intj=0;j< columnCount;j++){
temp= rs.getObject(j+1);
}
}
} finally {
if (!rs==null){
rs.close();
}
if (!stmt==null){
stmt.close();
}
if (!conn==null){
conn.close();
}
}
}