Oracle PL/SQL基础知识

Oracle PL/SQL基础知识

过程(存储过程)

过程用于执行特定的操作,当建立过程时,既可以指定输入参数(in),也可以指定输出参数(out)。通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。可以使用create procedure命令来建立过程。

 

调用过程有两种方法:

exec 过程名(参数值,..)

call 过程名(参数值,..)

 

创建存储过程基本语法:

create or replace procedure 过程名(变量 in 变量类型,..,变量 out 变量类型) is

//定义变量

begin

//执行语句;

end;

/

特别说明:or replace在创建存储过程中可带也可不。带or replace是指在存储过程名字相同时其覆盖。不带则无法覆盖。在使用or replace时要小心,建议不使用or replace对存储过程进覆盖。

 

过程创建举例:

--输入员工编号,新工资,修改员工的工资
create or replace procedure p4(v_in_emp in number,v_in_new_sal in number) is
v_ename varchar2(30);
begin
  select ename into v_ename from emp2 where empno = v_in_emp;
  update emp2 set sal = v_in_new_sal where empno = v_in_emp;
exception
  when no_data_found then
       dbms_output.put_line('员工编号不存在');
end;
/

 

特别说明:当编写过程出现错误时,查看具体错误信息。输入show error;

 

Java调用过程
/**
 * 调用过程
 */
package com.oracle.db;
import java.sql.*;
public class db4 {
	// 调用p4过程
	public static void main(String[] args) {
		Connection ct = null;
		CallableStatement cs = null;
		try {
			// 加载驱动
			Class.forName("oracle.jdbc.driver.OracleDriver");
			// 得到连接
			ct = DriverManager.getConnection(
					"jdbc:oracle:thin:@127.0.0.1:1521:Switch", "scott",
					"123456");
			// 创建CallableStatement
			cs = ct.prepareCall("{call p4(?,?)}");
			// 给?赋值
			cs.setInt(1, 7369);
			cs.setFloat(2, 1000.5f);
			// 执行
			cs.execute();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				// 关闭资源
				if (cs != null) {
					cs.close();
				}
				if (ct != null) {
					ct.close();
				}
			} catch (Exception e2) {
				e2.printStackTrace();
			}
		}
	}
}


扩展SQLHelper类,使之实现无返回参数的存储过程的方法

package com.oracle.db;
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;

public class SQLHelper {
	// 定义变量
	private static Connection ct = null;
	private static PreparedStatement ps = null;
	private static ResultSet rs = null;
	private static CallableStatement cs = null;

	// 连接数据库的用户名,密码,url,驱动
	// 说明:在实际开发中,我们往往把这些变量写到一个外部文件中
	// 当程序启动时,我们读入这些配置信息。java.util.Properites
	private static String username;
	private static String password;
	private static String driver;
	private static String url;

	// 使用静态块加载驱动(驱动只需要加载一次)
	static {
		// 使用Properties类,来读取配置文件
		Properties pp = new Properties();
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("dbinfo.properties");
			// 让pp与dbinfo.properties文件关联起来
			pp.load(fis);
			// 获取dbinfo.properties文件内信息
			username = (String) pp.getProperty("username");
			password = (String) pp.getProperty("password");
			driver = (String) pp.getProperty("driver");
			url = (String) pp.getProperty("url");

			// 获得驱动
			Class.forName(driver);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null) {
					fis.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			fis = null;
		}
	}

	// 调用存储过程的方法
	public static void executeProcedure(String sql, String[] parameters) {
		try {
			ct = DriverManager.getConnection(url,username,password);
			cs = ct.prepareCall(sql);
			if (parameters != null && !"".equals(parameters)) {
				for(int i = 0; i < parameters.length; i++){
					cs.setString(i + 1, parameters[i]);
				}
			}
			//执行
			cs.execute();
		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		} finally {
			close(rs, cs, ct);
		}
	}

	// 统一的cud操作
	public static void executeUpdate(String sql, String[] parameters) {
		try {
			ct = DriverManager.getConnection(url, username, password);
			ps = ct.prepareStatement(sql);
			if (parameters != null) {
				for (int i = 0; i < parameters.length; i++) {
					ps.setString(i + 1, parameters[i]);
				}
			}		
			// 执行
			ps.executeUpdate();

		} catch (Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e.getMessage());
		} finally {
			close(rs, ps, ct);
		}
	}

	// 写一个方法,完成查询任务
	// sql表示要执行的sql语句
	// sql select * from emp where ename=?
	public static ResultSet executeQuery(String sql, String[] parameters) {
		try {
			// 根据实际情况我们对sql语句?赋值
			// 得到连接
			ct = DriverManager.getConnection(url, username, password);
			// 创建ps对象,得到sql语句对象
			ps = ct.prepareStatement(sql);
			// 如果parameters不为null,才赋值
			if (parameters != null) {
				for (int i = 0; i < parameters.length; i++) {
					ps.setString(i + 1, parameters[i]);
				}
			}
			rs = ps.executeQuery();
		} catch (SQLException e) {
			e.printStackTrace();
			// 抛出运行异常
			throw new RuntimeException(e.getMessage());
		} finally {
			// close(rs,ps,ct);
		}
		return rs;
	}

	// 把关闭资源写成函数
	public static void close(ResultSet rs, Statement ps, Connection ct) {
		// 关闭资源
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			rs = null;
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			ps = null;
		}
		if (ct != null) {
			try {
				ct.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			ct = null;
		}
	}

	public static Connection getCt() {
		return ct;
	}

	public static PreparedStatement getPs() {
		return ps;
	}
}

dbinfo.propeties中的属性

driver = oracle.jdbc.driver.OracleDriver

url = jdbc:oracle:thin:@127.0.0.1:1521:Switch

username = scott

password =123456


dbinfo.propeties中的属性
driver = oracle.jdbc.driver.OracleDriver
url = jdbc:oracle:thin:@127.0.0.1:1521:Switch
username = scott
password =123456

函数

函数用于返回特定的数据,当建立函数时,在函数头部必须包含return子句,而在函数体内必须包含return语句返回的数据。我们可以使用create function来建立函数。

建立函数的基本语法:

create function 函数名(参数1,...)

return 数据类型 is

定义变量;

begin

执行语句;

end;

/

 

函数调用的基本语法:

select 函数名(参数,...) from dual;

 

实例:

--用函数接收用户名,返回用户年薪
create or replace function f1(v_in_ename varchar2)
return number is
v_annual_sal number;
begin
  select (sal+nvl(comm,0))*13 into v_annual_sal from emp2 where ename  = v_in_ename;
  return v_annual_sal;
end;
/

函数和过程的区别:

1、函数必须有返回值,而过程可以没有;

2、函数和过程在java中调用的方式不一样;

java中调用oracle函数可以在select语句中直接调用,如:select 自定义的函数名(参数) from 表;

过程则是使用CallableStatement完成调用。

/**
 * 使用SQLHelper调用函数
 */
package com.oracle.db;
import java.sql.*;
public class db6 {
	public static void main(String[] args) {
		String sql = "select f1('SMITH') from dual";
		ResultSet rs = SQLHelper.executeQuery(sql, null);
		try {
			if(rs.next()){
				System.out.println(rs.getDouble(1));
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

包用于在逻辑上组合过程和函数等对象,它由包的规范和包体两部分组成。

1、使用create package命令来创建包

建包基本语法:

create [or replace] package 包名 is

procedure 过程名(变量名 变量类型,...);

function 函数名(变量名 变量类型,...) return 数据类型;

end;

/

包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包体用于实现包规范的过程和函数。

 

实例:

--声明一个包
create or replace package pack1 is
--声明一个过程用于输入员工姓名,新工资,修改员工的工资
       procedure p1(v_in_ename in varchar2,v_in_newsal number);
--声明一个函数用于接收用户名,返回用户年薪
       function f1(v_in_ename in varchar2) return number;
end;
/

2、建立包体可以使用create package body 命令

建立包体基本语法:

create or replace package body 包名 is

procedure 过程名(变量名 变量类型,...) is

--声明变量;

begin

--执行语句;

exception

when 异常名 then

--异常处理;

end;

function 函数名(变量名 变量类型,...)

return 数据类型 is

--声明变量;

begin

--执行语句;

end;

end;

/


实例:

--建立包体
create or replace package body pack1 is
--实现一个过程用于输入员工姓名,新工资,修改员工的工资
       procedure p1(v_in_ename in varchar2,v_in_newsal number) is
       begin
         update emp2 set sal = v_in_newsal where ename = v_in_ename;
       end;
--实现一个函数用于接收用户名,返回用户年薪
       function f1(v_in_ename in varchar2) return number is
       v_annual_sal number;
       begin
         select (sal+nvl(comm,0))*13 into v_annual_sal from emp2 where ename = v_in_ename;
       return v_annual_sal;
       end;
end;
/

细节说明:

1、包体中要实现的函数或过程,应当在包规范中声明;

2、在调用包中的某个函数或过程的时候,需要使用对应的方法才可以调用。

3、如何调用包的过程或函数

当调用包的过程或是函数时,在过程和函数前需要带有包名,如果要访问其它方案的包,还需要在包名前加方案名。

调用基本方法:

exec 方案名.包名.过程名(参数,...);

call 方案名.包名.函数名(参数,...);

也可以直接用select 方案名.包名.函数名(参数,...) from dual;


/**
 * 使用SQLHelper调用包中函数
 */
package com.oracle.db;
import java.sql.*;
public class db7 {
	public static void main(String[] args) {
		String sql = "select scott.pack1.f1('SMITH') from dual";
		ResultSet rs = SQLHelper.executeQuery(sql, null);
		try {
			if(rs.next()){
				System.out.println(rs.getDouble(1));
			}
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

触发器

触发器是指隐含的执行的存储过程。当定义触发器时,必须要指定触发的事件和触发的操作,常用的触发事件包括insert/update/delete语句,而触发操作实际就是一个pl/sql块。可以使用create trigger 来建立触发器。

PS:触发器也是用来维护表的完整性的,触发器将在之后单独写成一篇博客。

定义并使用变量

在编写pl/sql程序时,可以定义变量和常量;在pl/sql程序中包括有:

1、标量类型(scalar)

2、复合类型(composite)

3、参照类型(reference)

4、lob(large object)

标量(scalar)

在编写pl/sql块时,如果要使用变量,需在定义部分定义变量。

pl/sql中定义变量和常量的语法如下:

identifier [constant] datatype [not null] [:=|default expr]

说明:

identifier:名称

constant:指定常量。需要指定它的初始值,且其值是不能改变的。

datatype:数据类型

not null:指定变量值不能为null

:=给变量或是常量指定初始值

default:用于指定初始值

expr:指定初始值的pl/sql表达式,可是文本值、其它变量、函数等。

 

标量定义的案例:

1、定义一个变长字符串

v_ename varchar2(10);

2、定义一个小数范围-9999.99~9999.99

v_sal number(6,2);

3、定义一个小数并给一个初始值为5.4 :=是pl/sql的赋值号

v_sal2 number(6,2):=5.4

4、定义一个日期类型的数据

v_hiredate date;

5、定义一个布尔变量,不能为空,初始值为false

v_valid boolean not null default false;

特别说明:pl/sql在定义一个变量的时候,如果要赋初值,则需要使用:=,如果只是=则是用于判断两个值是否相等。


实例:

--编写过程,以输入的员工号,显示员工的姓名、工资、个人所得税(税率为0.03)
create or replace procedure p5(v_in_empno in number) is
v_ename varchar2(30);
v_sal number;
v_tax_rate number(3,2) := 0.03;
v_tax number;
begin
  select ename,sal into v_ename,v_sal from emp2 where empno = v_in_empno;
  v_tax := v_sal * v_tax_rate;
  dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal || ' 员工的个人所得税为: ' || v_tax);
end;
/

%type类型

为了降低pl/sql程序的维护工作量,可以使用%type属性定义变量,这样它会按照数据库列来确定你定义的变量的类型和长度。

 

%type类型使用的基本语法:

标识符名 表名.列名%type;

--优化
create or replace procedure p6(v_in_empno in number) is
v_ename emp2.ename%type;    --使用emp2中ename的类型定义
v_sal emp2.sal%type;        --使用emp2中sal的类型定义
v_tax_rate emp2.sal%type := 0.03;   --使用emp2中sal的类型定义
v_tax emp2.sal%type;        --使用emp2中sal的类型定义
begin
  select ename,sal into v_ename,v_sal from emp2 where empno = v_in_empno;
  v_tax := v_sal * v_tax_rate;
  dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal || ' 员工的个人所得税为: ' || v_tax);
end;
/

复合变量(composite)

用于存放多个值的变量。常用的包括:1、pl/sql记录;2、pl/sql表

 

复合类型--pl/sql记录

类似与高级语言中的结构体,需要注意的是,当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)如下:

复合变量定义基本语法:

type 自定义的pl/sql记录名 is record(

变量名 变量类型,

变量名 变量类型

);

 

复合变量基本使用语法:

变量名 自定义的pl/sql记录名;

 

--复合类型
--编写过程,接收员工编号,显示该员工的名字、薪水、工作岗位(用pl/sql记录实现)
create or replace procedure p7(v_in_empno in emp2.ename%type) is
--定义记录类型
type emps_record is record
(
     v_ename emp2.ename%type,
     v_sal emp2.sal%type,
     v_job emp2.job%type
);
--定义emps_record记录类型的变量
emps emps_record;
begin
  select ename,sal,job into emps.v_ename,emps.v_sal,emps.v_job from emp2 where empno = v_in_empno;
  dbms_output.put_line('员工的姓名为:' || emps.v_ename || ' 员工的工资为:' || emps.v_sal || ' 员工的工作为: ' || emps.v_job);
end;
/

复合类型--pl/sql

相当于高级语言中的数组,但是需要注意的是在高级语言中数组的下标不能为负数,而pl/sql是可以为负数的,并且表元素的下标没有限制。(可以理解为是oracle下的数组)实例如下:

复合类型pl/sql表的基本语法:

type 自定义的pl/sql表名 is table of 对应表.列名%type

index by binary_integer;


declare
type table_type is table of emp2.ename%type
index by binary_integer;
stable table_type;--定义一个变量
begin
select ename into stable(-1) from emp where empno=7788;
dbms_output.put_line('员工名:'||stable(-1));
end;

说明:

table_type pl/sql表类型

emp.ename%type 指定了表的元素的类型和长度

stable pl/sql表变量

stable(0) 则表示下标为0的元素


参照变量

参照变量是指用于存放数值指针的变量,通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用的空间。在编写pl/sql程序时,可以使用游标变量(ref cursor)对象类型变量(ref obj_type)两种参照变量类型。

 

游标变量

通过游标可以取得返回结果集(这个结果集,往往是select语句的结果)的任何一行数据,从而提供共享的效率。

 

游标(ref cursor)使用

定义游标基本语法:

type 自定义游标名 is ref cursor;

变量名 自定义游标名;

 

打开游标基本语法:

open 游标变量 for select 语句;

 

取出当前游标指向的行基本语法:

fetch 游标变量 into 其它变量;

 

判断游标是否指向记录最后基本语法:

游标变量%notfound

 

使用游标时,当定义游标时不需要指定相应的select语句,但是当使用游标时,(open时)需要指定select语句,这样一个游标就写一个select语句结合了

--参照变量
--编写过程,输入部门号,显示该部门所有员工的姓名和工资
create or replace procedure p8(v_in_deptno in dept2.deptno%type) is
--定义游标变量类型
type emp_cursor is ref cursor;
--定义游标变量
v_emp_cursor emp_cursor;
v_ename emp2.ename%type;
v_sal emp2.sal%type;
begin
  --打开游标
  open v_emp_cursor for select ename,sal from emp2 where deptno = v_in_deptno;
  --用循环语句取出游标中指向的每行数据
  loop
    --取出每行数据
    fetch v_emp_cursor into v_ename,v_sal;
    --判断游标退出
    exit when v_emp_cursor%notfound;
    --输出每行数据
    dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal);
  end loop;
  --关闭游标
  close v_emp_cursor;
end;
/

--参照变量
--编写过程,输入部门号,显示该部门所有员工的姓名和工资并且如果某个员工的工资低于2000则增加100
create or replace procedure p9(v_in_deptno in dept2.deptno%type) is
--定义游标变量类型
type emp_cursor is ref cursor;
--定义游标变量
v_emp_cursor emp_cursor;
v_ename emp2.ename%type;
v_sal emp2.sal%type;
v_empno emp2.empno%type;
begin
  --打开游标
  open v_emp_cursor for select ename,sal,empno from emp2 where deptno = v_in_deptno;
  --用循环语句取出游标中指向的每行数据
  loop
    --取出每行数据
    fetch v_emp_cursor into v_ename,v_sal,v_empno;
    --判断游标退出
    exit when v_emp_cursor%notfound;
    --如果某个员工的工资低于2000则增加100
    if(v_sal < 2000) then
             update emp2 set sal = sal + 100 where empno = v_empno;
    end if;
    --输出每行数据
    dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal);
  end loop;
  --关闭游标
  close v_emp_cursor;
end;
/


----------参考《韩顺平玩转Oracle》


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值