一、存储过程:
(1)存储过程的创建
基本语法
CREATE [OR REPLACE] PROCEDURE procedure_name
(parameter1_name [mode] datatype
[DEFAULT|:=value]
[, parameter2_name [mode] datatype
[DEFAULT|:=value],…])
AS|IS
/*Declarative section is here */ --变量声明
BEGIN
/*Executable section is here*/
EXCEPTION
/*Exception section is here*/
END[procedure_name];
(1). IN(默认参数模式)这是一种输入类型的参数,参数值由调用方传入,并且只能被存储过程读取。这种参数模式是最常用的,也是默认的参数模式,关键字in位于参数名之后。参数的类型不能指定长度,在调用或执行这种in模式的存储过程时,用户需要向存储过程中传递若干参数值,以保证执行部分有具体的数值参与数据操作 。
设计一个in类型参数的存储过程并将它执行。
CREATE OR REPLACE PROCEDURE proc1(i in number)
AS
a varchar2(50);
BEGIN
a:=‘ ‘;
for j in 1..i loop
a:=a||’*’;
dbms_output.put_line(a);
end loop;
End;
/
Ececute proc1(6);
(2). OUT模式参数,这是一种输出类型的参数,表示这个参数在存储过程中已经被赋值,并且这个参数值可以传递到当前存储过程以外的环境中,关键字out位于参数名之后。
设计一个out类型参数的存储过程并将它执行。
CREATE OR REPLACE PROCEDURE proc2(j out int)
AS
BEGIN
j:=100;
dbms_output.put_line(j);
End;
/
Declare
K number;
Begin
Proc2(k);
End;
/
(3). IN OUT模式参数,在执行存储过程时,in参数不能被修改,它只能根据被传入的指定值(或是默认值)为存储过程提供数据,而out类型的参数只能等待被赋值,而不能像in参数那样为存储过程本身提供数据,但in out参数可以兼顾其它两种参数的特点,在调用存储过程时,可以从外界向该类型的参数传入值;在执行完存储过程之后,可以将该参数的返回值传给外界。
设计一个in out类型参数的存储过程并将它执行。
CREATE OR REPLACE PROCEDURE proc3(p1 in out
Number,p2 in out number)
iS
v_temp number;
BEGIN
v_temp:=p1
p1:=p2;
p2:=v_temp;
End;
/
Declare
num1 number:=100;
num2 number:=200;
Begin
Proc3(num1,num2);
dbms_output.put_line(‘num1=’||num1);
dbms_output.put_line(‘num2=’||num2);
End;
/
存储过程的创建实例
例如,创建一个存储过程,以部门号为参数,查询该部门的平均工资,并输出该部门中比平均工资高的员工号、员工名
CREATE OR REPLACE PROCEDURE show_emp(
p_deptno emp.deptno%TYPE)
AS
v_sal emp.sal%TYPE;
BEGIN
SELECT avg(sal) INTO v_sal FROM emp
WHERE deptno=p_deptno;
DBMS_OUTPUT.PUT_LINE(p_deptno||' '||'average salary is:'
||v_sal);
FOR v_emp IN (SELECT * FROM emp
WHERE deptno=p_deptno AND sal>v_sal) LOOP
DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '||v_emp.ename);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('The department doesn’’t exists!');
END show_emp;
例如,创建一个存储过程,以部门号为参数,返回该部门的人数和平均工资。
CREATE OR REPLACE PROCEDURE return_deptinfo(
p_deptno emp.deptno%TYPE,
p_avgsal OUT emp.sal%TYPE,
p_count OUT emp.sal%TYPE)
AS
BEGIN
SELECT avg(sal),count(*) INTO p_avgsal,p_count
FROM emp
WHERE deptno=p_deptno;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('The department don’’t exists!');
END return_deptinfo;
存储过程的调用
在SQL*PLUS中调用
EXEC procedure_name(parameter_list)
EXECUTE show_emp(10)
在PL/SQL块中调用
BEGIN
procedure_name(parameter_list);
END;
注意
在PL/SQL程序中,存储过程可以作为一个独立的表达式被调用。
DECLARE
v_avgsal emp.sal%TYPE;
v_count NUMBER;
BEGIN
show_emp(20);
return_deptinfo(10,v_avgsal,v_count);
DBMS_OUTPUT.PUT_LINE(v_avgsal||' '|| v_count);
END;
存储过程的管理
修改存储过程
CREATE OR REPLACE PROCEDURE procedure_name
查看存储过程及其源代码
查询数据字典视图USER_SOURCE
SELECT name,text FROM user_source
WHERE type='PROCEDURE';
重新编译存储过程
ALTER PROCEDURE…COMPILE
ALTER PROCEDURE show_emp COMPILE;
删除存储过程
DROP PROCEDURE
DROP PROCEDURE show_emp;
函数
(1)函数的创建
基本语法为
CREATE [OR REPLACE] FUNCTION function_name
(parameter1_name [mode] datatype
[DEFAULT|:=value]
[, parameter2_name [mode] datatype
[DEFAULT|:=value],…])
RETURN return_datatype
AS|IS
/*Declarative section is here */
BEGIN
/*Executable section is here*/
EXCEPTION
/*Exception section is here*/
END [function_name];
注意
在函数定义的头部,参数列表之后,必须包含一个RETURN语句来指明函数返回值的类型,但不能约束返回值的长度、精度、刻度等。如果使用%TYPE,则可以隐含地包括长度、精度、刻度等约束信息;
在函数体的定义中,必须至少包含一个RETURN 语句,来指明函数返回值。也可以有多个RETURN语句,但最终只有一个RETURN语句被执行。
例1,创建一个函数,并执行该函数。
CREATE OR REPLACE FUNCTION fun_hello return varchar2
Is
BEGIN
RETURN ‘你好,朋友’;
END;
/
Sqlplus中调用该函数
Select fun_hello from dual;
PL/SQL中调用该函数
Declare
ss varchar2(20);
Begin
ss:=fun_hello;
dbms_output.put_line(ss);
End;
/
定义一个函数,用于计算emp表中指定某个部门的平均工资。
SQL> create or replace function get_avg_pay(num_deptno number)
return number is
2 num_avg_pay number;
3 begin
4 select avg(sal) into num_avg_pay from emp where deptno=num_deptno;
5 return(round(num_avg_pay,2));
6 exception
7 when no_data_found then
8 dbms_output.put_line('该部门编号不存在');
9 return(0);
10 end;
11 /
加粗样式
函数可以在SQL语句的以下部分调用:
SELECT语句的目标列;
WHERE和HAVING子句;
CONNECT BY,START WITH,ORDER BY,GROUP BY子句;
INSERT语句的VALUES子句中;
UPDATE语句的SET子句中。
调用函数:
SQL> set serveroutput on
SQL> declare
2 avg_pay number;
3 begin
4 avg_pay:=get_avg_pay(10);
5 dbms_output.put_line('平均工资是:'||avg_pay);
6 end;
7 /
平均工资是:2916.67
PL/SQL 过程已成功完成。
函数的管理
函数的修改
CREATE OR REPLACE FUNCTION function_name
查看函数及其源代码
查询数据字典视图USER_SOURCE
SELECT name,text FROM user_source
WHERE type='FUNCTION';
函数重编译
ALTER FUNCTION…COMPILE
ALTER FUNCTION ret_maxsal COMPILE;
删除函数
DROP FUNCTION
DROP FUNCTION ret_maxsal;
局部子程序
例如,在一个块内部定义一个函数和一个过程。函数以部门号为参数返回该部门的平均工资;过程以部门号为参数,输出该部门中工资低于部门平均工资的员工的员工号、员工名。
DECLARE
v_deptno emp.deptno%TYPE;
v_avgsal emp.sal%TYPE;
FUNCTION return_avgsal(p_deptno emp.deptno%TYPE)
RETURN emp.sal%TYPE
AS
v_sal emp.sal%TYPE;
BEGIN
SELECT avg(sal) INTO v_sal FROM emp
WHERE deptno=p_deptno;
RETURN v_sal;
END return_avgsal;
PROCEDURE show_emp(p_deptno emp.deptno%TYPE)
AS
CURSOR c_emp IS
SELECT * FROM emp WHERE deptno=p_deptno;
BEGIN
FOR v_emp IN c_emp LOOP
IF v_emp.sal<return_avgsal(v_emp.deptno) THEN
DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '||
v_emp.ename);
END IF;
END LOOP;
END show_emp;
BEGIN
v_deptno:=&x;
v_avgsal:=return_avgsal(v_deptno);
show_emp(v_deptno);
END;
存储子程序与局部子程序区别在于:
存储子程序己经编译好放在数据库服务器端,可以直接调用,而局部子程序存在于定义它的语句块中,在运行时先进行编译;
存储子程序不能重载,而局部子程序可以进行重载;
存储子程序可以被任意的PL/SQL块调用,而局部子程序只能在定义它的块中被调用。
在一个PL/SQL块中重载两个过程,一个以员工号为参数,输出该员工信息;另一个以员工名为参数,输出员工信息。利用这两个过程分别查询员工号为7902,7934,以及员工名为SMITH,FORD的员工信息 。
在一个PL/SQL块中重载两个过程,一个以员工号为参数,输出该员工信息;另一个以员工名为参数,输出员工信息。利用这两个过程分别查询员工号为7902,7934,以及员工名为SMITH,FORD的员工信息 。
DECLARE
PROCEDURE show_empinfo(p_empno emp.empno%TYPE)
AS
v_emp emp%ROWTYPE;
BEGIN
SELECT * INTO v_emp FROM emp
WHERE empno=p_empno;
DBMS_OUTPUT.PUT_LINE(v_emp.ename||' '||v_emp.deptno);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('There is not such an employee!');
END show_empinfo;
PROCEDURE show_empinfo(p_ename emp.ename%TYPE)
AS
v_emp emp%ROWTYPE;
BEGIN
SELECT * INTO v_emp FROM emp
WHERE ename=p_ename;
DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '||v_emp.deptno);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('There is not such an employee!');
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('There are more than one employee!');
END show_empinfo;
BEGIN
show_empinfo(7902);
show_empinfo(7934);
show_empinfo('SMITH');
show_empinfo('FORD');
END ;
包
包的创建
(1)创建包规范
语法
CREATE OR REPLACE PACKAGE package_name
IS|AS
[PRAGMA SERIALLY_RESUABLE]
type_definition|variable_declaration|
exception_declaration|cursor_declaration|
procedure_ declaration|function_ declaration
END [package_name];
创建包规范
注意:
元素声明的顺序可以是任意的,但必须先声明后使用;
所有元素是可选的;
过程和函数的声明只包括原型,不包括具体实现。
例1、创建一个程序包的规范,首先在该程序包中声明一个可以获取指定部门的平均工资的函数,然后再声明一个可以实现按照指定比例上调指定职务的工资的存储过程。
CREATE OR REPLACE PACKAGE pack_emp iS
function fun_avg_sal(num_deptno number)retrun number;
procedure pro_regulate_sal(var_job varchar2,num_proportion number);
End pack_emp;
/
**(2)创建包体 **
语法
CREATE OR REPLACE PACKAGE BODY package_name
IS|AS
[PRAGMA SERIALLY_RESUABLE]
type_definition|variable_declaration|
exception_declaration|
cursor_declaration|
procedure_definition |
function_definition
END [package_name];
注意:
包体中函数和过程的原型必须与包规范中的声明完全一致;
只有在包规范已经创建的条件下,才可以创建包体;
如果包规范中不包含任何函数或过程,则可以不创建包体。
例2、创建程序包pack_emp的主体,在该主体中实现对“规范”中声明的函数和存储过程。
CREATE OR REPLACE PACKAGE BODY pack_emp is
Function fun_avg_sal(num_deptno number) return number is
num_avg_sal number;
Begin
select avg(sal)
into num_avg_sal
from emp
where deptno=num_deptno;
return(num_avg_sal);
Exception
when no_data_found then
dbms_output.put_line(‘该部门编号不存在雇员记录’);
return 0;
End fun_avg_sal;
PROCEDURE pro_regulate_sal(var_job varchar2,num_proportion number) is
Begin
update emp
set sal=sal*(1+num_proprotion)
where job=var_job;
End pro_regulate_sal;
End pack_emp;
/
(3)包的调用
在包规范声明的任何元素是公有的,在包外都是可见的
包外:通过package.element形式调用;
包内:直接通过元素名进行调用。
在包体中定义而没有在包头中声明的元素是私有的,只能在包体中引用
例3、创建一个匿名的PL/SQL块,然后通过程序包pack_emp调用其中的函数fun_avg_sal和存储过程pro_regulate_sal,并输出函数的返回结果。
SQL>set serveroutput on
SQL>declare
num_deptno emp.deptno%type;
var_job emp.job%type;
num_avg_sal emp.sal%type;
num_proportion number;
begin
num_deptno:=10;
num_avg_sal:=pack_emp.fun_avg_sal(num_deptno);
dbms_output.put_line(num_deptno||’号部门的平均工资是:’||num_avg_sal);
var_job:=‘SALESMAN’;
num_proprotion:=0.1;
pack_emp.pro_regulate_sal(var_job,num_proportion);
end;
(4)包重载
在一个包中重载两个过程,分别以部门号和部门名称为参数,查询相应部门员工名、员工号信息。
CREATE OR REPLACE PACKAGE pkg_overload
AS
PROCEDURE show_emp(p_deptno NUMBER);
PROCEDURE show_emp(p_dname VARCHAR2);
END pkg_overload;
重载子程序必须同名不同参,即名称相同,参数不同。参数不同体现为参数的个数、顺序、类型等不同。
如果两个子程序参数只是名称和模式不同,则不能重载。
PROCEDURE overloadme(parameter1 IN NUMBER);
PROCEDURE overloadme(parameter2 OUT NUMBER);
不能根据两个函数返回类型不同而对它们进行重载。
FUNCTION overloadme RETURN DATE;
FUNCTION overloadme RETURN NUMBER;
重载子程序参数必须在类型系列方面有所不同。
PROCEDURE overloadchar(parameter IN CHAR);
PROCEDURE overloadchar(parameter IN VARCHAR2);
CREATE OR REPLACE PACKAGE BODY pkg_overload
AS
PROCEDURE show_emp(p_deptno NUMBER)
AS
BEGIN
FOR v_emp IN (SELECT * FROM emp WHERE deptno=p_deptno) LOOP
DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '|| v_emp.ename);
END LOOP;
END show_emp;
PROCEDURE show_emp(p_dname VARCHAR2)
AS
v_deptno NUMBER;
BEGIN
SELECT deptno INTO v_deptno FROM dept
WHERE dname=p_dname;
FOR v_emp IN (SELECT * FROM emp
WHERE deptno=v_deptno) LOOP
DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '||
v_emp.ename);
END LOOP;
END show_emp;
END pkg_overload;
(5) 包的初始化
包在第一次被调用时从磁盘读取到共享池,并在整个会话的持续期间保持。在此过程中,可以自动执行一个初始化过程,对软件包进行实例化。
包的初始化过程只在包第一次被调用时执行,因此也称为一次性过程,它是一个匿名的PL/SQL块,在包体结构的最后,以BEGIN开始。
示例
在pkg_emp包中,在包初始化时给minsal和maxsal两个变量赋值,在子程序中直接引用这两个变量。
CREATE OR REPLACE PACKAGE pkg_emp
AS
minsal NUMBER;
maxsal NUMBER;
e_beyondbound EXCEPTION;
PROCEDURE update_sal(
p_empno NUMBER, p_sal NUMBER);
PROCEDURE add_employee(
p_empno NUMBER,p_sal NUMBER);
END pkg_emp;
CREATE OR REPLACE PACKAGE BODY pkg_emp
AS
PROCEDURE update_sal(p_empno NUMBER, p_sal NUMBER)
AS
BEGIN
IF p_sal BETWEEN minsal AND maxsal THEN
UPDATE emp SET sal=p_sal WHERE empno=p_empno;
IF SQL%NOTFOUND THEN
RAISE_APPLICATION_ERROR(-20000,'The employee doesn''t exist');
END IF;
ELSE
RAISE e_beyondbound;
END IF;
EXCEPTION
WHEN e_beyondbound THEN
DBMS_OUTPUT.PUT_LINE('The salary is beyond bound!');
END update_sal;
PROCEDURE add_employee(p_empno NUMBER,p_sal NUMBER)
AS
BEGIN
IF p_sal BETWEEN minsal AND maxsal THEN
INSERT INTO emp(empno,sal) VALUES(p_empno,p_sal);
ELSE
RAISE e_beyondbound;
END IF;
EXCEPTION
WHEN e_beyondbound THEN
DBMS_OUTPUT.PUT_LINE('The salary is beyond bound!');
END add_employee;
BEGIN
SELECT min(sal), max(sal) INTO minsal,maxsal FROM emp;
END pkg_emp;
(6)包的管理
包的修改
CREATE OR REPLACE PACKAGE package_name
查看包及其源代码
查询数据字典视图USER_SOURCE
SELECT name,text FROM user_source WHERE type='PACKAGE';
SELECT name,text FROM user_source WHERE type='PACKAGE BODY';
重新编译包
ALTER PACKAGE…COMPILE(包规范和包体)
ALTER PACKAGE…COMPILE SPECIFICATION(包规范)
ALTER PACKAGE… COMPILE BODY(包体)
ALTER PACKAGE pkg_emp COMPILE;
ALTERPACKAGE pkg_emp COMPILE SPECIFICATION;
ALTER PACKAGE pkg_emp COMPILE BODY;
删除包
DROP PACKAGE (包规范和包体)
DROP PACKAGE BODY (包体)
DROP PACKAGE BODY pkg_emp;
DROP PACKAGE pkg_emp;