Oracle学习 第16天
—— PL/SQL 编程(列表返回 & 分页查询)
PL/SQL 常见存储过程
PL/SQL 中常见的的存储过程依据返回值的类型可以分为三类。
一、无返回值。
如之前案例中所写,在存储过程中对数据表进行修改删除等功能时,是不需要返回值的。
SQL> SELECT * FROM emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
3 TESTFor
2 TESTFor
1 TESTFor
7369 SMITH CLERK 7902 1980/12/17 1800.00 20
7499 ALLEN SALESMAN 7698 1981/2/20 1600.00 300.00 30
7521 WARD SALESMAN 7698 1981/2/22 1250.00 500.00 30
SQL>
SQL> CREATE OR REPLACE PROCEDURE pro_test(v_in_empno NUMBER) IS
2 BEGIN
3 UPDATE emp SET sal = 3500 WHERE empno = v_in_empno;
4 END;
5
6 /
Procedure created
SQL> exec pro_test(2);
PL/SQL procedure successfully completed
SQL> SELECT * FROM emp;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
----- ---------- --------- ----- ----------- --------- --------- ------
3 TESTFor
2 TESTFor 3500.00
1 TESTFor
7369 SMITH CLERK 7902 1980/12/17 1800.00 20
7499 ALLEN SALESMAN 7698 1981/2/20 1600.00 300.00 30
7521 WARD SALESMAN 7698 1981/2/22 1250.00 500.00 30
二、非列表返回值。
即返回单列。之前写的大部分关于查询的存储过程都是单列返回值。
我们可以定义一个变量去接收。
SQL> CREATE OR REPLACE PROCEDURE pro_test(v_in_empno NUMBER) IS
2 v_name emp.ename%TYPE;
3 BEGIN
4 SELECT ename INTO v_name FROM emp WHERE empno = v_in_empno;
5 dbms_output.put_line(v_name);
6 END;
7 /
Procedure created
SQL> exec pro_test(7369);
SMITH
PL/SQL procedure successfully completed
我们也可以使用OUT参数。无需定义变量。
SQL>
SQL> CREATE OR REPLACE PROCEDURE pro_test(v_in_empno IN NUMBER, v_out_name OUT VARCHAR2) IS
2 BEGIN
3 SELECT ename INTO v_out_name FROM emp WHERE empno = v_in_empno;
4 dbms_output.put_line(v_out_name);
5 END;
6 /
Procedure created
SQL>
SQL> DECLARE
2 v_empno NUMBER;
3 v_name VARCHAR2(20);
4 BEGIN
5 v_empno := 7499;
6 pro_test(v_empno, v_name);
7 dbms_output.put_line('工号:' || v_empno || '的员工姓名为:' || v_name);
8 END;
9 /
ALLEN
工号:7499的员工姓名为:ALLEN
PL/SQL procedure successfully completed
JAVA 代码调用带返回值的存储过程(主要代码)
private static Connection conn = null;
private static ResultSet rs = null;
private static CallableStatement cs = null;
private static String username = "scott";
private static String password = "scott";
private static String driver = "oracle.jdbc.driver.OracleDriver";
private static String url = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL";
try {
conn = DriverManager.getConnection(url, username, password);
//创建CallableStatement
//这里固定格式 :调用语句写在 {} 中, call 存储过程名(参数) 参数使用?占位符
cs = conn.prepareCall("{call pro_test(?,?)}");
//参数赋值_第一个参数 输入参数
cs.setString(1,"7499");
//参数赋值_第二个参数 输出参数
//注意:第一个参数为问号占位符的序号(从1开始数,第二个问号),第二个参数为该输出参数在Oracle中的类型。注意类型的书写方法
cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);
//执行
cs.execute();
//取出 存储过程 返回的值
//调用CallableStatement的 Get** 方法,由于我们的存储过程返回的是VARCHAR类型,所以这里使用 getString
//getString 方法中的参数 是问号占位符的位置, 我们的输出参数是第二个问号,所以这里参数写 2
String name = cs.getString(2);
System.out.println("用户名为:" + name);
} catch (Exception e) {
} finally {
//关闭资源(省略代码-自行补充)
}
三、列表返回值。
Oracle 存储过程是没有返回值的,它的所有的返回值都是通过OUT参数来替代的,如上例中所示。
但是,如果返回值的是一个列表,不是一个单一的值,就不能用一般的OUT参数了,而要用到PACKAGE与游标类型。
示例:输入部门号,返回该部门的所有员工信息。(以JAVA调用为例)
PL/SQL 代码:
SQL>
SQL> -- 创建包,包中定义一个游标类型
SQL> CREATE OR REPLACE PACKAGE pack_test IS
2 -- 定义游标类型
3 TYPE my_cursor IS REF CURSOR;
4 END;
5 /
Package created
SQL>
SQL> -- 编写过程 包含两个参数 输入参数 --部门编号; 输出参数 --游标类型的结果
SQL> CREATE OR REPLACE PROCEDURE pro_test(v_in_deptno IN NUMBER, v_out_result OUT pack_test.my_cursor) IS
2 BEGIN
3 -- 打开游标
4 OPEN v_out_result FOR SELECT * FROM emp WHERE deptno = v_in_deptno;
5 -- close v_out_result; --切记游标不要在此处关闭,否则第三方程序将无法使用(如JAVA),应在第三方调用代码中关闭
6 END;
7 /
Procedure created
JAVA 代码:(主要代码)
try {
conn = DriverManager.getConnection(url, username, password);
cs = conn.prepareCall("{call pro_test(?,?)}");
// 赋值 --输入值 10号部门
cs.setInt(1, 10);
// 赋值 --输出值 游标类型
cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
cs.execute();
// 获取返回结果 没有游标类型 只能获取Object类型,然后强转
rs = (ResultSet) cs.getObject(2);
// 循环读取
while (rs.next()) {
System.out.println(rs.getString("ENAME") + rs.getShort("JOB"));
}
} catch (Exception e) {
} finally {
//关闭资源(包括关闭游标)
}
PL/SQL 分页查询的存储过程
示例:编写存储过程,实现输入表名、每页要显示的记录数、当前页;返回查询到的结果集。
★ 类型是通用的,前面的例子已经创建过一次,所以这里其实没有必要再次创建,可以直接使用,但为了保证例子完整,这里将上面的代码复制下来。
-- 创建包,包中定义一个游标类型 CREATE OR REPLACE PACKAGE pack_test IS
-- 定义游标类型
TYPE my_cursor IS REF CURSOR;
END;
-- 编写存储过程
CREATE OR REPLACE PROCEDURE pro_test(v_in_tableName IN VARCHAR2, v_in_pageSize IN NUMBER, v_in_pageIndex IN NUMBER, v_out_result OUT pack_test.my_cursor) IS
-- 定义变量
v_sql VARCHAR2(4000);
v_startIndex NUMBER;
v_endIndex NUMBER;
BEGIN
v_startIndex := (v_in_pageIndex - 1) * v_in_pageSize + 1;
v_endIndex := v_startIndex + v_in_pageSize - 1;
v_sql := 'SELECT T.ROWNUM, T.* FROM v_in_tableName T WHERE T.ROWNUM BETWEEN' || v_startIndex || 'AND' || v_endIndex ;
-- 打开游标,让游标指向结果集
OPEN v_out_result FOR v_sql;
END;
JAVA 调用
try {
conn = DriverManager.getConnection(url, username, password);
//四个参数 表名、每页的记录数、当前页、返回的结果
cs = conn.prepareCall("{call pro_test(?,?,?,?)}");
// 赋值 --输入值 表名
cs.setString(1, "emp");
// 赋值 --输入值 每页的记录数
cs.setInt(2, 5);
// 赋值 --输入值 当前页码
cs.setInt(3, 1);
// 赋值 --输出值 游标类型
cs.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);
cs.execute();
// 获取返回结果 没有游标类型 只能获取Object类型,然后强转
rs = (ResultSet) cs.getObject(4);
// 循环读取
while (rs.next()) {
System.out.println(rs.getString("ENAME") + rs.getShort("JOB"));
}
} catch (Exception e) {
} finally {
close(rs, cs, conn);
}
}