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 createdJAVA 代码:(主要代码)
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); } }