(韩顺平讲解)pl/sql编程(二)

时间:2021-10-18 00:06:04
一、pl/sql进阶—控制结构
pl/sql中提供了三种条件分支语句 if -- then, if --then---else, if---then---elsif---else
(1)简单的条件判断 if – then
问题:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该员工工资增加10%。
Sql代码
1. create or replace procedure sp_pro6(spName varchar2) is
2. --定义
3. v_sal emp.sal%type;
4. begin
5. --执行
6. select sal into v_sal from emp where ename=spName;
7. --判断
8. if v_sal<2000 then
9. update emp set sal=sal+sal*10% where ename=spName;
10. end if;
11. end;

(2)二重条件分支 if—then--else
问题:编写一个过程,可以输入一个雇员名,如果该雇员的补助不是0就在原来的基础上增加100;如果补助为0就把补助设为200;
Sql代码
1. create or replace procedure sp_pro6(spName varchar2) is
2. --定义
3. v_comm emp.comm%type;
4. begin
5. --执行
6. select comm into v_comm from emp where ename=spName;
7. --判断
8. if v_comm<>0 then
9. update emp set comm=comm+100 where ename=spName;
10. else
11. update emp set comm=comm+200 where ename=spName;
12. end if;
13. end;

(3)多重条件分支 if--then--elsif--else
问题:编写一个过程,可以输入一个雇员编号,如果该雇员的职位是PRESIDENT就给他的工资增加1000,如果该雇员的职位是MANAGER就给他的工资增加500,其它职位的雇员工资增加200。
Sql代码
1. create or replace procedure sp_pro6(spNo number) is
2. --定义
3. v_job emp.job%type;
4. begin
5. --执行
6. select job into v_job from emp where empno=spNo;
7. if v_job='PRESIDENT' then
8. update emp set sal=sal+1000 where empno=spNo;
9. elsif v_job='MANAGER' then
10. update emp set sal=sal+500 where empno=spNo;
11. else
12. update emp set sal=sal+200 where empno=spNo;
13. end if;
14. end;

二、循环语句 –while循环
基本循环至少要执行循环体一次,而对于while循环来说,只有条件为true时,才会执行循环体语句,while循环以while...loop开始,以end loop结束。
问题:请编写一个过程,可以输入用户名,并循环添加10个用户到users表中,用户编号从11开始增加。
Sql代码
1. create or replace procedure sp_pro6(spName varchar2) is
2. --定义 :=表示赋值
3. v_num number:=11;
4. begin
5. while v_num<=20 loop
6. --执行
7. insert into users values(v_num,spName);
8. v_num:=v_num+1;
9. end loop;
10. end;

三、顺序控制语句-goto ,null
①goto语句
goto语句用于跳转到特定标号去执行语句.注意由于使用goto语句会增加程序的复杂性,并使得应用程序可以读性变差,所以在做一般应用开发时,建议大家不要使用goto语句.
基本语法如下 goto lable,其中lable是已经定义好的标号名.
declare
i int :=1;
begin
loop
dbms_output.put_line(‘输出i=‘||i);
if i=10 then
goto end_loop;
end if;
i:=i+1;
end loop;
<<end_loop>>
dbms_output.put_line('循环结束');
end;
注意:--in:表示这是一个输入参数,默认为in
--out:表示一个输出参数

四、oracle视图
创建视图
create view 视图名 as select语句 [with read only]
创建或修改视图
create or replace view 视图名 as select语句 [with read only]
删除视图
drop view 视图名

五、pl/sql进阶—例外处理
Exception
When <异常情况名>then
<异常处理代码>
When <异常情况名>then
<异常处理代码>
……
when others then
<异常处理代码>
例外情况名 错误代码 描述
NO_DATA_FOUND ORA-01403 对于SELECT 叙述没有传回任何值。
TOO_MANY_ROWS ORA-01427 只允许传回一笔记录的SELECT 叙述结果却多于一笔。
INVALID_CURSOR ORA-01001 使用非法的的光标操作。
VALUE_ERROR ORA-06502 出现数值、数据形态转换、撷取字符串或强制性的错误。
INVALID_NUMBER ORA-01722 字符串到数值的转换失败。
ZERO_DIVIDE ORA-01476 被零除。
DUP_VAL_ON_INDEX ORA-00001 试图向具有唯一键值的索引中插入一个重复键值。
CASE_NOT_FOUND ORA-xxxxx 没有case条件匹配

CURSOR_NOT_OPEN ORA-xxxxxx 游标没有打开

各种案例:
1.请写一个过程,可以向book表添加书,要求通过java程序调用该过程。
--编写过程
Sql代码
create or replace procedure sp_pro7(spBookId in number,spbookName in varchar2,sppublishHouse in varchar2) is
begin
insert into book values(spBookId,spbookName,sppublishHouse);
end;
--在java中调用

package com.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

public class Procedure_07_Test {
	
	public static void main(String[] args) {
		try {
			
			Class.forName("oracle.jdbc.driver.OracleDriver");
			Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORCL", "scott", "0108");
			CallableStatement cs = conn.prepareCall("{call sp_pro7(?,?,?)}");
			
			//给?赋值
			cs.setInt(1, 10);
			cs.setString(2, "天龙八部");
			cs.setString(3, "清华大学出版社");
			
			//执行
			cs.execute();
			
			cs.close();
			conn.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
2.案例:编写一个过程,可以输入雇员的编号,返回该雇员的姓名。
案例扩张:编写一个过程,可以输入雇员的编号,返回该雇员的姓名、工资和岗位。
Sql代码
--有输入和输出的存储过程
create or replace procedure sp_pro8
(spno in number, spName out varchar2) is
begin
select ename into spName from emp where empno=spno;
end
--在java中调用
package com.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

public class Procedure_08_Test {
	
	public static void main(String[] args) {
		
		try {
			
			Class.forName("oracle.jdbc.driver.OracleDriver");
			Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl", 
					"scott", "0108");
			CallableStatement cs = conn.prepareCall("{call sp_pro8(?,?)}");
			
			//给第一个?赋值
			cs.setInt(1, 7788);
			//给第二个?赋值   
			cs.registerOutParameter(2, oracle.jdbc.OracleTypes.VARCHAR);
			//执行   
			cs.execute();
			
			//取出返回值,要注意?的顺序
			String name = cs.getString(2);
			System.out.println(name);

			cs.close();
			conn.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
3.案例:有返回值的存储过程(列表[结果集])。编写一个过程,输入部门号,返回该部门所有雇员信息。
由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,
但由于是集合,所以不能用一般的参数,必须要用pagkage了,步骤如下:
①建一个包。
②建立存储过程。
③下面看看如何在java程序中调用
1.建立一个包,在该包中,我定义类型test_cursor,是个游标。 如下:
Sql代码
create or replace package testpackage as
TYPE test_cursor is ref cursor;
end testpackage;
2.建立存储过程。如下:
Sql代码
create or replace procedure sp_pro9(spNo in number,p_cursor out testpackage.test_cursor) is
begin
open p_cursor for
select * from emp where deptno = spNo;
end sp_pro9;
3.如何在java程序中调用该过程
package com.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

public class Procedure_09_Test {
	
	public static void main(String[] args) {
	
		try {

			Class.forName("oracle.jdbc.driver.OracleDriver");
			Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
					"scott", "0108");
			CallableStatement cs = ct.prepareCall("{call sp_pro9(?,?)}");
			
			cs.setInt(1, 10);
			cs.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR);
			
			cs.execute();
			
			ResultSet set = (ResultSet) cs.getObject(2);
			while (set.next()) {
				System.out.println(set.getInt(1)+ " " + set.getString(2));
				
			}
			
			cs.close();
			ct.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
4.编写分页过程
Sql代码
select t1.*, rownum rn from (select * from emp) t1 where rownum<=10;
select * from (select t1.*, rownum rn from (select * from emp) t1 where rownum<=10) where rn>=6;
--开发一个包
--建立一个包,在该包中,我定义类型test_cursor,是个游标。 如下:
Sql代码
create or replace package testpackage as
TYPE test_cursor is ref cursor;
end testpackage;
--开始编写分页的过程
create or replace procedure fenye
(tableName in varchar2,
pagesize in number,--一页显示记录数
pageNow in number,
myrows out number,--总记录数
myPageCount out number,--总页数
p_cursor out testpackage.test_cursor--返回的记录集
) is
--定义部分
--定义sql语句 字符串
v_sql varchar2(1000);
--定义两个整数
v_begin number:=(pageNow-1)*pagesize+1;
v_end number:=pageNow*pagesize;
begin
--执行部分
v_sql:='select * from (select t1.*, rownum rn from (select * from '||tableName||') t1 where rownum<='||v_end||') where rn>='||v_begin;
--把游标和sql关联
open p_cursor for v_sql;
--计算myrows和myPageCount
--组织一个sql语句
v_sql:='select count(*) from '||tableName;
--执行sql,并把返回的值,赋给myrows;
execute immediate v_sql into myrows;
--计算myPageCount
if mod(myrows,pagesize)=0 then
myPageCount:=myrows/pagesize;
else
myPageCount:=myrows/pagesize+1;
end if;
--关闭游标
close p_cursor;
end;
--使用java测试
package com.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

public class Procedure_fenye_Test {
	
	public static void main(String[] args) {
		
		try {
			
			Class.forName("oracle.jdbc.driver.OracleDriver");
			Connection ct = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl",
					"scott", "0108");
			CallableStatement cs = ct.prepareCall("{call fenye(?,?,?,?,?,?)}");
			
			cs.setString(1, "emp");
			cs.setInt(2, 5);
			cs.setInt(3, 2);
			//注册总记录数 
			cs.registerOutParameter(4, oracle.jdbc.OracleTypes.INTEGER);
			//注册总页数 
			cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER);
			//注册返回的结果集
			cs.registerOutParameter(6, oracle.jdbc.OracleTypes.CURSOR);
			
			cs.execute(); 
			
			//取出总记录数   /这里要注意,getInt(4)中4,是由该参数的位置决定的 
			int rowNum = cs.getInt(4);
			int pageCount = cs.getInt(5);
			ResultSet rs = (ResultSet) cs.getObject(6);
			
			System.out.println("rowNum:" + rowNum);
			System.out.println("pageCount:" + pageCount);
			
			while(rs.next()){
				System.out.println("编号:"+rs.getInt(1)+" 名字:"+rs.getString(2)+" 工资:"+rs.getFloat(6));   
			}
			
			cs.cancel();
			ct.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

控制台输出:

rowNum:14
pageCount:3
编号:7698 名字:BLAKE 工资:2850.0
编号:7782 名字:CLARK 工资:2450.0
编号:7788 名字:SCOTT 工资:4678.0
编号:7839 名字:KING 工资:6000.0
编号:7844 名字:TURNER 工资:1500.0

这里注意下,在创建过程时,游标不能关闭,否则java测试是报异常,提示找不到游标。所以,close p_cursor;这句在执行sql语句时应该注释掉。

--新的需要,要求按照薪水从低到高排序,然后取出6-10
过程的执行部分做下改动,如下:
Sql代码
begin
--执行部分
v_sql:='select * from (select t1.*, rownum rn from (select * from '||tableName||' order by sal) t1 where rownum<='||v_end||') where rn>='||v_begin;
重新执行一次procedure,java不用改变,运行,控制台输出:
rowNum:14
pageCount:3
编号:7934 名字:MILLER 工资:1300.0
编号:7844 名字:TURNER 工资:1500.0
编号:7499 名字:ALLEN 工资:1600.0
编号:7782 名字:CLARK 工资:2450.0
编号:7698 名字:BLAKE 工资:2850.0