您的位置:首页 > 数据库

pl/sql编程学习笔记(二)

2016-06-27 13:45 302 查看
针对上一篇的各种代码块,进行整合Oracle子程序分为两种:过程、函数

1 子程序

函数与过程实现上最大的区别就在于,函数是可以有返回值的,而过程只能依靠OUT 或者 INOUT来返回数据
过程
过程=过程的声明+PLSQL块

语法:
CREATE[OR REPLACE] PROCEDURE 过程名称([参数名称[参数模式]NOCOPY数据类型])
[AUTHID[DEFINER|CURENT_USER]]
AS|IS
声明部分;
BEGIN
程序部分
EXCEPTION
异常部分
END;
/
exec 过程名  --执行创建的过程

ps: 过程创建如果出现错误,使用show errors 显示错误详细信息, 重新编译子程序 ALTER PROCEDURE 过程名称 COMPILE ;

示例:
CREATE OR REPLACE PROCEDURE dept_insert_proc(
p_dno dept.deptno%TYPE,
p_dna dept.dname%TYPE,
p_dlo dept.loc%TYPE)
AS
v_deptCount	NUMBER ;		-- 保存COUNT()函数结果
BEGIN
SELECT COUNT(deptno) INTO v_deptCount FROM dept WHERE deptno=p_dno ;	-- 统计
IF v_deptCount > 0 THEN			-- 有此编号的部门
RAISE_APPLICATION_ERROR(-20789,'增加失败:该部门已存在!') ;
ELSE
INSERT INTO dept(deptno,dname,loc) VALUES (p_dno,p_dna,p_dlo) ;
DBMS_OUTPUT.put_line('新部门增加成功!') ;
COMMIT ;
END IF ;
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.put_line('SQLERRM = ' || SQLERRM) ;
ROLLBACK ;				-- 事务回滚
END ;
/

函数:

语法:
CREATE[OR REPLACE] FUNCTION 函数名称([参数名称[参数模式]NOCOPY数据类型])
[AUTHID[DEFINER|CURENT_USER]]
AS|IS
声明部分;
BEGIN
程序部分
EXCEPTION
异常部分
END;
/


示例:过程调用函数
CREATE OR REPLACE PROCEDURE invoke_proc
AS
v_salary	NUMBER ;  --定义接收的变量
BEGIN
v_salary := get_salary_fun(7369) ; --调用自定义的函数  函数根据ID查询雇员信息
DBMS_OUTPUT.put_line('雇员7369的工资为:' || v_salary) ;
END ;
/


查询过程对象SQL

SELECT object_name,created,timestamp,status
FROM user_objects --数据字典
WHERE object_type='PROCEDURE' OR object_type='FUNCTION';


查询子程序的相关源
SELECT * FROM user_source WHERE name='proc_name' ;  --查看过程
SELECT * FROM user_source WHERE name='func_name' ;  --查看函数


删除子程序

DROP PROCEDURE proc_name;
DROP FUNCTION func_name;


参数模式
IN(默认,数值传递):在子程序之中锁做的修改不会影响原始参数的内容;参数无法回传
OUT(空进带值出):不带任何数值到子程序之中,子程序可以通过此变量讲数值返回给调用处;
IN OUT(地址传递):可以将值到子程序之中,同事也会将子程序之中对变量的修改返回到调用处;

--定义一个过程
CREATE OR REPLACE PROCEDURE in_proc(
p_paramA IN VARCHAR2,-- 明确定义IN参数模式
p_paramB VARCHAR2-- 默认的参数模式为IN
AS
BEGIN
DBMS_OUTPUT.put_line('执行in_proc()过程:p_paramA = ' || p_paramA) ;
DBMS_OUTPUT.put_line('执行in_proc()过程:p_paramB = ' || p_paramB) ;
END ;
/
--顶一个PL/SQL块
DECLARE
v_titleA VARCHAR2(50) := 'Java开发实战经典';
v_titleB VARCHAR2(50) := 'Android开发实战经典' ;
BEGIN
in_proc(v_titleA , v_titleB) ;
END ;
/

--使用default设置默认值
CREATE OR REPLACE PROCEDURE in_proc(
p_paramA IN VARCHAR2 , 
p_paramB VARCHAR2 DEFAULT 'Oracle开发实战经典')-- 设置默认值
AS
BEGIN
DBMS_OUTPUT.put_line('执行in_proc()过程:p_paramA = ' || p_paramA) ;
DBMS_OUTPUT.put_line('执行in_proc()过程:p_paramB = ' || p_paramB) ;
END ;
/
--少传一个参数调用,默认设置起作用
DECLARE
v_titleAVARCHAR2(50) := 'Java开发实战经典';
BEGIN
in_proc(v_titleA ) ;
END ;
/


OUT参数模式
CREATE OR REPLACE PROCEDURE out_proc(
p_paramA OUT VARCHAR2,		-- OUT参数模式
p_paramB OUT VARCHAR2)		-- OUT参数模式
AS
BEGIN
DBMS_OUTPUT.put_line('执行out_proc()过程:p_paramA = ' || p_paramA) ;
DBMS_OUTPUT.put_line('执行out_proc()过程:p_paramB = ' || p_paramB) ;
p_paramA := 'Java开发实战经典' ;	-- 此值将返回给实参
p_paramB := 'Android开发实战经典' ;	-- 此值将返回给实参
END ;
/
DECLARE
v_titleA	VARCHAR2(100) := '此处只是声明一个接收返回数据的标记';
v_titleB	VARCHAR2(100) := '此内容不会传递到过程,但是过程会将修改内容传回' ;
BEGIN
out_proc(v_titleA , v_titleB) ;
DBMS_OUTPUT.put_line('调用out_proc()过程之后变量内容:v_titleA = ' || v_titleA) ;
DBMS_OUTPUT.put_line('调用out_proc()过程之后变量内容:v_titleB = ' || v_titleB) ;
END ;
/


IN OUT参数模式 (Java中的引用传递)
CREATE OR REPLACE PROCEDURE inout_proc(
p_paramA IN OUT VARCHAR2,	-- IN OUT参数模式
p_paramB IN OUT VARCHAR2 )	-- IN OUT参数模式
AS
BEGIN
DBMS_OUTPUT.put_line('执行inout_proc()过程:p_paramA = ' || p_paramA) ;
DBMS_OUTPUT.put_line('执行inout_proc()过程:p_paramB = ' || p_paramB) ;
p_paramA := 'Java开发实战经典' ;	-- 此值将返回给实参
p_paramB := 'Android开发实战经典' ;	-- 此值将返回给实参
END ;
/
DECLARE
v_titleA	VARCHAR2(50) := 'Java WEB开发实战经典';
v_titleB	VARCHAR2(50) := 'Oracle开发实战经典' ;
BEGIN
inout_proc(v_titleA , v_titleB) ;
DBMS_OUTPUT.put_line('调用inout_proc()过程之后变量内容:v_titleA = ' || v_titleA) ;
DBMS_OUTPUT.put_line('调用inout_proc()过程之后变量内容:v_titleB = ' || v_titleB) ;
END ;
/

返回嵌套表类型的

DECLARE
TYPE emp_nested IS TABLE OF emp%ROWTYPE ;
v_emp_return	emp_nested ;
FUNCTION dept_emp_fun(p_dno emp.deptno%TYPE) RETURN emp_nested
AS
v_emp_temp	emp_nested ;
BEGIN
SELECT * BULK COLLECT INTO v_emp_temp FROM emp WHERE deptno=p_dno ;
RETURN v_emp_temp ;
END ;
BEGIN
BEGIN
v_emp_return := dept_emp_fun(10) ;
FOR x IN v_emp_return.FIRST .. v_emp_return.LAST LOOP
DBMS_OUTPUT.put_line('雇员编号:' || v_emp_return(x).empno || ',姓名:' || v_emp_return(x).ename || ',职位:' || v_emp_return(x).job) ;
END LOOP ;
EXCEPTION
WHEN others THEN
DBMS_OUTPUT.put_line('此部门没有雇员!') ;
END ;
END ;
/

返回嵌套表类型

DECLARE
v_sum	NUMBER ;
FUNCTION add_fun(p_num NUMBER)
RETURN NUMBER
AS
BEGIN
IF p_num = 1 THEN
RETURN 1 ;
ELSE
RETURN p_num + add_fun(p_num - 1) ;
END IF ;
END ;
BEGIN
v_sum := add_fun(100) ;		-- 进行1~100累加
DBMS_OUTPUT.put_line('累加结果:' || v_sum) ;
END ;
/


NOCOPY 将IN于OUT 由基于复制变成基于引用,若程序异常也可以执行下去
DECLARE
TYPE dept_nested IS TABLE OF dept%ROWTYPE ;
v_dept		dept_nested ;
PROCEDURE useNocopy_proc(p_temp IN OUT NOCOPY dept_nested)
IS
BEGIN
-- 相关程序代码
END ;
BEGIN
SELECT * BULK COLLECT INTO v_dept FROM dept ;	-- 将雇员表全部数据拷贝到嵌套表之中
v_dept.EXTEND(2000000,1) ;			-- 将集合扩充,数据以第1条记录为准进行填充
useNocopy_proc(v_dept) ;				-- 使用NOCOPY
END ;
/
DECLARE
v_varA	NUMBER := 10 ;
v_varB	NUMBER := 20 ;
PROCEDURE change_proc(
p_paramINOUT IN OUT NUMBER ,
p_paramNOCOPY IN OUT NOCOPY NUMBER)
IS
BEGIN
p_paramINOUT := 100 ;
p_paramNOCOPY := 100 ;
RAISE_APPLICATION_ERROR(-20001 , '测试NOCOPY.') ;
END ;
BEGIN
DBMS_OUTPUT.put_line('【过程调用之前】v_varA = ' || v_varA || ',v_varB = ' || v_varB) ;
BEGIN
change_proc(v_varA , v_varB) ;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('SQLCODE = ' || SQLCODE || ',SQLERRM = ' || SQLERRM) ;
END ;
DBMS_OUTPUT.put_line('【过程调用之后】v_varA = ' || v_varA || ',v_varB = ' || v_varB) ;
END ;
/

自治事务

在ORACLE之中每一个SESSION都拥有独立的事务,而在一个事务处理过程之中都会执行一系列的SQL更新操作,这些都收到一个整体的事务控制,其他用户如果要进行操作则必须在执行commit或rollback之后才可以。但是如果现在开发的子程序之中需要进行独立的子事务处理,并且在此事务处理过程之中执行commit活rollback不影响整体主事务,那么久需要通过自治事务进行控制
DECLARE
PROCEDURE dept_insert_proc AS
PRAGMA AUTONOMOUS_TRANSACTION;		-- 自治事务
BEGIN
-- 此处更新将使用自治事务,主事务将被挂起
INSERT INTO dept(deptno,dname,loc) VALUES (60,'cnn','南京') ;
COMMIT ;								-- 提交自治事务
END ;
BEGIN
INSERT INTO dept(deptno,dname,loc) VALUES (50,'开发部','天津') ;
dept_insert_proc() ;
ROLLBACK ;									-- 此处为主事务回滚
END ;
/

过程函数授权
--GRANT EXECUTE ON c##scott.proc_name TO c##otheruser ;
CREATE OR REPLACE PROCEDURE proc_name
AUTHID CURRENT_USER AS --
BEGIN
INSERT INTO func(ename,job,sal,comm) VALUES ('哈哈','程序员',11111,2222) ;
COMMIT ;
END ;
/


使用JDBC调用存储过程
package cn.ys.demo;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Types;
public class ProcDemo {
// 定义Oracle的数据库驱动程序
public static final String DBDRIVER = "oracle.jdbc.driver.OracleDriver" ;
// 定义Oracle数据库的连接地址
public static final String DBURL = "jdbc:oracle:thin:@localhost:1521:orcl" ;
// Oracle数据库的连接用户名
public static final String DBUSER = "c##scott" ;
// Oracle数据库的连接密码
public static final String DBPASS = "tiger" ;
public static void main(String[] args) throws Exception {
Connection conn = null ;							// 数据库连接
CallableStatement cstmt = null ;					// 数据库操作
String sql = "{CALL proc_name(?,?,
bef4
?)}" ;			// 调用过程
Class.forName(DBDRIVER) ;							// 加载驱动程序
// 连接MySQL数据库时,要写上连接的用户名和密码
conn = DriverManager.getConnection(DBURL, DBUSER, DBPASS);
cstmt = conn.prepareCall(sql) ;						// 实例化对象
cstmt.setInt(1, 70) ;								// 设置第一个参数是70
cstmt.setInt(2, 80) ;								// 设置第一个参数是80
cstmt.registerOutParameter(2, Types.INTEGER) ;		// 设置返回值类型
cstmt.registerOutParameter(3, Types.INTEGER) ;		// 设置返回值类型
cstmt.execute() ;									// 执行存储过程
System.out.println("INOUT的返回值:" + cstmt.getInt(2));
System.out.println("OUT的返回值:" + cstmt.getInt(3));
cstmt.close() ;										// 操作关闭
conn.close() ;										// 数据库关闭
}
}


2 包的概念

oracle中包的定义

包规范:定义包中可以被外部访问的部分,在包规范中声明的内容可以从应用程序和包的任何地方访问,其语法如下:
CREATE [OR REPLACE] PACKAGE 包名称
|AUTHID CURRENT_USER | DEFINER
IS|AS
结构名称定义(类型,过程,函数,游标,异常等)
END[包名称];
/


包体:负责包规范中定义的函数或过程的具体代码实现,如果在包中定义了规范中没有的内容,则此部分被设为私有访问,包体语法如下:
CREATE [OR REPLACE] PACKAGE BODY 包名称
IS|AS
结构实现(类型,过程,函数,游标,异常等)
BEGIN
<span style="white-space:pre">	</span>包初始化程序代码
END[包名称];
/


Demo:
--声明部分
CREATE OR REPLACE PACKAGE demo_pkg
AS
TYPE cursor_ref IS REF CURSOR ;
FUNCTION get_emp_fun(p_dno dept.deptno%TYPE) RETURN cursor_ref ;
END ;
/
--对声明的部分进行代码实现
CREATE OR REPLACE PACKAGE BODY demo_pkg
AS
FUNCTION get_emp_fun(p_dno dept.deptno%TYPE) RETURN SYS_REFCURSOR
AS
cur_var		SYS_REFCURSOR ;
BEGIN
OPEN cur_var FOR SELECT * FROM emp WHERE deptno=p_dno ;	-- 打开参数游标
RETURN cur_var ;
END ;
END ;
/


包的重载

--编写包规范,同时进行子程序重载
CREATE OR REPLACE PACKAGE emp_delete_pkg
AS
-- 删除雇员时所发生的异常
emp_delete_exception	EXCEPTION ;
-- 根据雇员编号删除雇员信息
PROCEDURE delete_emp_proc(p_empno emp.empno%TYPE) ;
-- 根据雇员姓名删除雇员信息
PROCEDURE delete_emp_proc(p_ename emp.ename%TYPE) ;
-- 根据雇员所在部门及职位删除雇员信息
PROCEDURE delete_emp_proc(p_deptno emp.deptno%TYPE , p_job emp.job%TYPE) ;
END ;
/
CREATE OR REPLACE PACKAGE BODY emp_delete_pkg
AS
-- 根据雇员编号删除雇员信息,如果没有数据被删除则抛出异常
PROCEDURE delete_emp_proc(p_empno emp.empno%TYPE) AS
BEGIN
DELETE FROM emp WHERE empno=p_empno ;
IF SQL%NOTFOUND THEN
RAISE emp_delete_exception ; --抛出异常
END IF ;
END delete_emp_proc ;
-- 根据雇员姓名删除雇员信息,如果没有数据被删除则抛出异常
PROCEDURE delete_emp_proc(p_ename emp.ename%TYPE) AS
BEGIN
DELETE FROM emp WHERE ename=UPPER(p_ename) ;
IF SQL%NOTFOUND THEN
RAISE emp_delete_exception ;
END IF ;
END delete_emp_proc ;
-- 根据部门编号和雇员职位删除雇员信息,如果没有数据被删除则抛出异常
PROCEDURE delete_emp_proc(p_deptno emp.deptno%TYPE , p_job emp.job%TYPE) AS
BEGIN
DELETE FROM emp WHERE deptno=p_deptno AND job=p_job ;
IF SQL%NOTFOUND THEN
RAISE emp_delete_exception ;
END IF ;
END delete_emp_proc ;
END ;
/

包的初始化功能:

--定义包规范
CREATE OR REPLACE PACKAGE init_pkg AS
-- 定义索引表类型,里面将保存多个dept行记录,使用数字作为索引类型
TYPE dept_index IS TABLE OF dept%ROWTYPE INDEX BY PLS_INTEGER ;
-- 定义要操作的游标
CURSOR dept_cur RETURN dept%ROWTYPE ;
-- 定义索引表变量
v_dept	dept_index ;
-- 定义部门增加操作函数,如果增加成功返回true,否则返回false
FUNCTION dept_insert_fun(p_deptno dept.deptno%TYPE , p_dname dept.dname%TYPE , p_loc dept.loc%TYPE) RETURN BOOLEAN ;
END ;
/
--实现定义的包规范
CREATE OR REPLACE PACKAGE BODY init_pkg AS
CURSOR dept_cur RETURN dept%ROWTYPE IS
SELECT * FROM dept ;
FUNCTION dept_insert_fun(p_deptno dept.deptno%TYPE , p_dname dept.dname%TYPE , p_loc dept.loc%TYPE) RETURN BOOLEAN AS
BEGIN
IF NOT v_dept.EXISTS(p_deptno) THEN	-- 数据不存在
INSERT INTO dept(deptno,dname,loc) VALUES (p_deptno,p_dname,p_loc) ;
v_dept(p_deptno).deptno := p_deptno ;
v_dept(p_deptno).dname := p_dname ;
v_dept(p_deptno).loc := p_loc ;
RETURN true ;
ELSE
RETURN false ;
END IF ;
END dept_insert_fun ;
BEGIN
-- 包初始化操作:将游标中的数据保存到索引表之中,同时以部门编号作为索引表操作索引
FOR dept_row IN dept_cur LOOP
v_dept(dept_row.deptno) := dept_row ;
END LOOP ;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('程序出现错误。') ;
END ;
/

包引用纯度级别:

CREATE OR REPLACE PACKAGE purity_pkg AS
-- 定义包中的变量
v_name VARCHAR2(10) := 'cnn' ;
-- 根据雇员编号删除雇员信息。但此函数不能执行更新操作
FUNCTION emp_delete_fun_wnds(p_empno emp.empno%TYPE) RETURN NUMBER ;
-- 根据雇员编号查找雇员信息。但此函数不能执行SELECT操作
FUNCTION emp_find_fun_rnds(p_empno emp.empno%TYPE) RETURN NUMBER ;
-- 使用新的内容修改v_name变量内容。但此函数不能修改包中的变量
FUNCTION change_name_fun_wnps(p_param VARCHAR2) RETURN VARCHAR2 ;
-- 读取v_name属性内容。但此函数不能读取包中变量
FUNCTION get_name_fun_rnps(p_param NUMBER) RETURN VARCHAR2 ;
PRAGMA RESTRICT_REFERENCES(emp_delete_fun_wnds, WNDS) ;		-- 设置函数纯度级别,不能执行更新操作
PRAGMA RESTRICT_REFERENCES(emp_find_fun_rnds, RNDS) ;		-- 设置函数纯度级别,不能执行SELECT操作
PRAGMA RESTRICT_REFERENCES(change_name_fun_wnps, WNPS) ;	-- 设置函数纯度级别,不能修改包中的变量
PRAGMA RESTRICT_REFERENCES(get_name_fun_rnps, RNPS) ;		-- 设置函数纯度级别,不能读取包中变量
END ;
/
--定义执行包,违反函数纯度规则
CREATE OR REPLACE PACKAGE BODY purity_pkg AS
-- 根据雇员编号删除雇员信息。但此函数不能执行更新操作
FUNCTION emp_delete_fun_wnds(p_empno emp.empno%TYPE) RETURN NUMBER AS
BEGIN	-- 此函数由于定义了wnds纯度,所以无法执行数据表更新操作
DELETE FROM emp WHERE empno=p_empno ;
RETURN 0 ;	-- 满足函数要求返回数据
END ;
-- 根据雇员编号查找雇员信息。但此函数不能执行SELECT操作
FUNCTION emp_find_fun_rnds(p_empno emp.empno%TYPE) RETURN NUMBER AS
v_emp	emp%ROWTYPE ;
BEGIN	-- 此函数由于定义了rnds纯度,所以无法执行数据表查询操作
SELECT * INTO v_emp FROM emp WHERE empno=p_empno ;
RETURN 0 ;	-- 满足函数要求返回数据
END ;
-- 使用新的内容修改v_name变量内容。但此函数不能修改包中的变量
FUNCTION change_name_fun_wnps(p_param VARCHAR2) RETURN VARCHAR2 AS
BEGIN	-- 此函数由于定义了wnps纯度,所以函数无法修改包中的v_name变量
v_name := p_param ;
RETURN '' ;	-- 满足函数要求返回数据
END ;
-- 读取v_name属性内容。但此函数不能读取包中变量
FUNCTION get_name_fun_rnps(p_param NUMBER) RETURN VARCHAR2 AS
BEGIN	-- 此函数由于定义了rnps,所以函数无法读取v_name变量
RETURN v_name ;
END ;
END ;
/


3 系统工具包

DBMS_OUTPUT包

DECLARE
v_line1		VARCHAR2(200) ;			-- 保存第1行数据
v_status	NUMBER ;			-- 保存状态
BEGIN
DBMS_OUTPUT.enable ;		-- 启用缓冲
DBMS_OUTPUT.put_line('此信息可以正常输出。') ;
--DBMS_OUTPUT.disable ;		-- 禁用缓冲
--DBMS_OUTPUT.put_line('此信息输出无法显示。') ;
DBMS_OUTPUT.put('www.') ;	--向缓冲区输入内容
DBMS_OUTPUT.put('www1.') ;	--向缓冲区输入内容
DBMS_OUTPUT.new_line ;		-- 换行,输出之前缓冲区内容
DBMS_OUTPUT.put('www2.') ;	--向缓冲区输入内容
DBMS_OUTPUT.get_line(v_line1 , v_status) ;	-- 读取缓冲区一行数据
END ;
/

DBMS_JOB包

DROP SEQUENCE job_seq ;				--删除序列
DROP TABLE job_data PURGE ;			--删除表,不允许恢复
CREATE SEQUENCE job_seq ;			--创建序列
CREATE TABLE job_data (				--创建表
jid		NUMBER ,
title		VARCHAR2(20) ,
job_date	DATE ,
CONSTRAINT pk_jid PRIMARY KEY(jid)
) ;

--定义过程,实现数据插入
CREATE OR REPLACE PROCEDURE insert_demo_proc(p_title job_data.title%TYPE) AS
BEGIN
INSERT INTO job_data(jid,title,job_date) VALUES (job_seq.nextval ,p_title, SYSDATE) ;
END ;
/
--定义一个job
DECLARE
v_jobno		NUMBER ;
BEGIN
DBMS_JOB.submit(v_jobno ,			-- 通过OUT模式返回创建的作业编号
'insert_demo_proc(''作业A'') ;' , 		-- 作业执行时需要调用的过程
SYSDATE ,					-- 作业开始时间
'SYSDATE + (1/(24*60*60))') ;		-- 作业操作间隔
DBMS_OUTPUT.put_line('作业编号:' || v_jobno) ;
COMMIT ; 						-- 必须执行此操作
END ;
/
--修改JOB_QUEUE_PROCESSES参数
ALTER SYSTEM SET JOB_QUEUE_PROCESSES =10 ;
--查询user_jobs数据字典
select job,next_date,broken,interval,what from user_jobs ;
--修改作业的运行间隔,每小时间隔一次
EXECUTE DBMS_JOB.interval(v_jobno , 'SYSDATE + (1/(24*60))') ;  --v_jobon为返回的作业编号
--删除作业
EXECUTE DBMS_JOB.remove(v_jobno) ;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: