oracle的plsql程序设计3

基于前两节文章(点击打开链接),对oracle中一般认识(这里是说与mysql作比较),这里会对oracle特殊部分做一些详细的了解:oracle的plsql的程序设计、oracle的存储过程、存储函数、触发器做一些了解;

一、plsql的程序设计

(一)、使用oracle的plsql的作用:由于是使用oracle数据库自己本身提供的程序,所以操作起来比其他外部的程序要快,效率高一些;

(二)、对oracle数据库的认识


(三)、oracle的plsql的基础语法

1、plsql中的结构


例如:helloWord

set serveroutput on

declare
   -- 说明部分
 begin
   --程序
   dbms_output.put_line('Hello World');
 end;
 /


2、plsql中的变量定义


eg:

引用型变量 

--查询7839的姓名和薪水
set serveroutput on

declare
  --定义变量保存姓名和薪水
  --pename varchar2(20);
  --psal   number;
  pename emp.ename%type;
  psal   emp.sal%type;
begin
  --得到姓名和薪水
  select ename,sal into pename,psal  from emp where empno=7839;

  dbms_output.put_line(pename||'的薪水是'||psal);
end;
/

记录型变量

--查询7839的姓名和薪水
set serveroutput on

declare
  --定义记录型变量:代表一行
  emp_rec emp%rowtype;
begin
  select * into emp_rec from emp where empno=7839;

  dbms_output.put_line(emp_rec.ename||'的薪水是'||emp_rec.sal);
end;
/


3、plsql中的分支  判断


eg:

--判断用户从键盘上输入的数字
set serveroutput on

--接收键盘输入
--num 是一个地址值,在该地址上保存了输入的值
accept num prompt '请输入一个数字';

declare
  --定义变量保存输入的数字
  pnum number := #
begin
  if pnum = 0 then dbms_output.put_line('您输入的是0');
    elsif pnum = 1 then dbms_output.put_line('您输入的是1');
    elsif pnum = 2 then dbms_output.put_line('您输入的是2');
    else dbms_output.put_line('其他数字');
  end if;

end;
/
  


4、plsql中的循环



--打印1~10
set serveroutput on

declare
  pnum number := 1;
begin
  loop
    --退出条件
    exit when pnum > 10;

    dbms_output.put_line(pnum);
    --加一
    pnum := pnum + 1;
  end loop;
end;
/







5、plsql中使用 光标 来接收一个集合的数据   把接收到的集合数据存储在光标中



/*
1. 光标属性
   %isopen    %rowcount(影响的行数)
   %found     %notfound
*/

不带参数的光标示例

--查询并打印员工的姓名和薪水
/*
1. 光标属性
   %isopen    %rowcount(影响的行数)
   %found     %notfound

*/
set serveroutput on
declare 
  --定义光标
  cursor cemp is select ename,sal from emp;
  pename emp.ename%type;
  psal   emp.sal%type;
begin
  open cemp;
  loop
    --取一条记录
    fetch cemp into pename,psal;
    --退出条件
    --exit when 没有取到记录;
    exit when cemp%notfound;

    dbms_output.put_line(pename||'的薪水是'||psal);

  end loop;
  close cemp;
end;
/
  


带参数 的光标示例

--查询某个部门的员工姓名
set serveroutput on

declare
  --定义光标保存某个部门的员工姓名
  cursor cemp(dno number) is select ename from emp where deptno=dno;
  pename emp.ename%type;
begin
  open cemp(20);
  loop
    fetch cemp into pename;
    exit when cemp%notfound;

    dbms_output.put_line(pename);

  end loop;
  close cemp;
end;
/



最后,来一个总的示例,给emp表的 员工涨工资:

--涨工资,总裁1000 经理800 其他400
set serveroutput on

declare
  --alter table "SCOTT"."EMP" rename column "JOB" to empjob
  cursor cemp is select empno,empjob from emp;
  pempno emp.empno%type;
  pjob   emp.empjob%type;
begin
  rollback;
  open cemp;
  loop  
    --取一条记录
    fetch cemp into pempno,pjob;
    exit when cemp%notfound;
    
    --判断职位
    if pjob = 'PRESIDENT' then update emp set sal=sal+1000 where empno=pempno;
      elsif pjob = 'MANAGER' then update emp set sal=sal+800 where empno=pempno;
      else update emp set sal=sal+400 where empno=pempno;
    end if;
  end loop;
  close cemp;
  
  --why?  --> ACID
  commit;
  dbms_output.put_line('完成');
end;
/

6、plsql中的例外,也就是异常


6.1、例外提供了程序的健壮性和容错性

6.2、oracle的例外有两种方式,一种是系统本身提供的、另一种是自己可以定义的类型



eg:

系统提供的异常

--被0除
set serveroutput on

declare
  pnum number;
begin
  pnum := 1/0;

exception
  when zero_divide then dbms_output.put_line('1:0不能做被除数');
                        dbms_output.put_line('2:0不能做被除数');
  when value_error then dbms_output.put_line('算术或者转换错误');
  WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('其他例外');
end;
/

自定义的例外

--查询50号部门的员工姓名
set serveroutput on

declare
  cursor cemp is select ename from emp where deptno=50;
  pename emp.ename%type;
  --自定义例外
  no_emp_found exception;
begin
  open cemp;
  --取第一条记录
  fetch cemp into pename;
  
  if cemp%notfound then
    --抛出例外
    raise no_emp_found;
  end if;
  
  --进程pmon (process monitor)
  close cemp;

exception
  when no_emp_found then dbms_output.put_line('没有找到员工');
  WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('其他例外');
end;
/






二、oracle 的存储过程和存储函数

1、说明,存储过程、存储函数、触发器也都是oracle的数据库对象;

(一)、存储过程的结构



简单的示例:

--打印Hello World
/*
调用存储过程
1. exec sayhelloworld();
2. begin
     sayhelloworld();
     sayhelloworld();
   end;
   /
*/
create or replace procedure sayhelloworld
as
   --说明部分
begin
   dbms_output.put_line('Hello World');
end;
/



带参数的存储过程
--给指定员工涨100,并且打印涨前和涨后的薪水
create or replace procedure raiseSalary(eno in number)
as
  --说明部分
  psal emp.sal%type;
begin
  --得到涨前的薪水
  select sal into psal from emp where empno=eno;
  
  update emp set sal=sal+100 where empno=eno;
  
  --要不要commit?
  
  dbms_output.put_line('涨前:'||psal||'   涨后:'||(psal+100));
end;
/


(二)、存储函数的结构



eg:

--查询某个员工的年收入
create or replace function queryEmpIncome(eno in number)
return number
as
   --月薪和奖金
   psal emp.sal%type;
   pcomm emp.comm%type;
begin
   select sal,comm into psal,pcomm from emp where empno=eno;

   --返回年收入
   return psal*12+nvl(pcomm,0);
end;
/


(三)、存储过程与存储函数中的返回值








--查询某个员工的姓名  月薪   职位

/*
思考:
1. 查询某个员工的所有信息 ---> out参数太多
2. 查询某个部门中的所有员工信息 ---> 返回集合
*/
create or replace procedure queryEmpInfo(eno in number,
                                         pename out varchar2,
                                         psal   out number,
                                         pjob   out varchar2)
as
begin
  select ename,sal,empjob into pename,psal,pjob from emp where empno=eno;
end;
/


(四)、使用包和包体来使存储过程out一光标





2. 查询某个部门中的所有员工信息 ---> 返回集合



包头
CREATE OR REPLACE PACKAGE MYPACKAGE AS 

  type empcursor is ref cursor;
  procedure queryEmpList(dno in number,empList out empcursor);

END MYPACKAGE;


包体
CREATE OR REPLACE PACKAGE BODY MYPACKAGE AS

  procedure queryEmpList(dno in number,empList out empcursor) AS
  BEGIN

    open empList for select * from emp where deptno=dno;

  END queryEmpList;

END MYPACKAGE;

(五)、使用jdbc调用存储过程和存储函数

package demo.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCUtils {

	private static String driver = "oracle.jdbc.OracleDriver";
	private static String url = "jdbc:oracle:thin:@192.168.56.101:1521:orcl";
	private static String user = "scott";
	private static String password = "tiger";
	static{
		try {
			Class.forName(driver);
		} catch (ClassNotFoundException e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public static Connection getConnection(){
		try {
			return DriverManager.getConnection(url, user, password);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return null;
	}

	/*
	 * 运行java程序:
	 * java -Xms100M -Xmx200M HelloWorld
	 * 
	 * 技术方向:
	 * 1. 性能调优
	 * 2. 故障诊断:ThreadDump
	 *           window: ctrl+break
	 *           linux: kill -3 pid
	 */
	public static void release(Connection conn,Statement st,ResultSet rs){
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				rs = null;// Java GC
			}
		}
		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				st = null;
			}
		}
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}finally{
				conn = null;
			}
		}
	}
}











package demo.oracle;

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

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleTypes;

import org.junit.Test;

import demo.util.JDBCUtils;

public class TestOracle {
	/*
	 * create or replace procedure queryEmpInfo(eno in number, pename out
	 * varchar2, psal out number, pjob out varchar2)
	 */
	@Test
	public void testProcedure() {
		// {call <procedure-name>[(<arg1>,<arg2>, ...)]}
		String sql = "{call queryEmpInfo(?,?,?,?)}";
		Connection conn = null;
		CallableStatement call = null;
		try {
			conn = JDBCUtils.getConnection();
			call = conn.prepareCall(sql);

			// 对于in参数,赋值
			call.setInt(1, 7839);

			// 对于out参数,申明
			call.registerOutParameter(2, OracleTypes.VARCHAR);
			call.registerOutParameter(3, OracleTypes.NUMBER);
			call.registerOutParameter(4, OracleTypes.VARCHAR);

			// 执行
			call.execute();

			// 取出结果
			String name = call.getString(2);
			double sal = call.getDouble(3);
			String job = call.getString(4);
			System.out.println(name + "\t" + sal + "\t" + job);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.release(conn, call, null);
		}
	}

	/*
	 * create or replace function queryEmpIncome(eno in number) return number
	 */
	@Test
	public void testFunction() {
		// {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
		String sql = "{?=call queryEmpIncome(?)}";
		Connection conn = null;
		CallableStatement call = null;
		try {
			conn = JDBCUtils.getConnection();
			call = conn.prepareCall(sql);

			// 对于out参数,申明
			call.registerOutParameter(1, OracleTypes.NUMBER);

			// 对于in参数,赋值
			call.setInt(2, 7839);

			// 执行
			call.execute();

			// 取出结果
			double income = call.getDouble(1);
			System.out.println(income);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.release(conn, call, null);
		}
	}

	@Test
	public void testCursor() {
		// {call <procedure-name>[(<arg1>,<arg2>, ...)]}
		
		String sql = "{call MYPACKAGE.queryEmpList(?,?)}";
		Connection conn = null;
		CallableStatement call = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			call = conn.prepareCall(sql);

			// 对于in参数,赋值
			call.setInt(1, 20);

			// 对于out参数,申明
			call.registerOutParameter(2, OracleTypes.CURSOR);

			// 执行
			call.execute();

			// 取出结果
			rs = ((OracleCallableStatement)call).getCursor(2);
			while(rs.next()){
				String name = rs.getString("ename");
				double sal = rs.getDouble("sal");
				System.out.println(name+"\t"+sal);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			JDBCUtils.release(conn, call, rs);
		}
	}

}

三、oracle的触发器


1、说明:触发器也是oracle的一个数据库对象

2、触发器的创建以及例子



一个简单的触发器的例子:

--每当成功插入新员工后,自动打印“成功插入新员工”

create trigger saynewemp
after insert
on emp
declare
begin
   dbms_output.put_line('成功插入新员工');
end;
/






利用触发器实施安全性的检查

/*
复杂的安全性检查
禁止在非工作时间插入新员工

1. 周末:to_char(sysdate,'day') in ('星期六','星期日')
2. 上班前 下班后:to_number(to_char(sysdate,'hh24')) not between 9 and 18
*/
create or replace trigger securityemp
before insert
on emp
begin
  if to_char(sysdate,'day') in ('星期六','星期日') or 
     to_number(to_char(sysdate,'hh24')) not between 9 and 18 then
     --禁止插入
    raise_application_error(-20001,'禁止在非工作时间插入新员工');
  end if;
end;
/

利用触发器实施数据的确认


/*
数据确认
涨后的薪水不能少于涨前的薪水
*/
create or replace trigger checksalary
before update
on emp
for each row
begin
  --if 涨后的薪水  < 涨前的薪水  then 
  if :new.sal  < :old.sal then
    raise_application_error(-20002,'涨后的薪水不能少于涨前的薪水.涨前:'||:old.sal||'  涨后:'||:new.sal);
  end if;
end;
/





猜你喜欢

转载自blog.csdn.net/liangwanmian/article/details/79252640