过程与函数(另外还有包与触发器)是命名的PL/SQL块(也是用户的方案对象),被编译后存储在数据库中,以备执行。因此,其它PL/SQL块可以按名称来使用他们。所以,可以将商业逻辑、企业规则写成函数或过程保存到数据库中,以便共享。
过程和函数统称为PL/SQL子程序,他们是被命名的PL/SQL块,均存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。在本节中,主要介绍:
1. 创建存储过程和函数。
2. 正确使用系统级的异常处理和用户定义的异常处理。
3. 建立和管理存储过程和函数。
- 创建函数
语法如下:
CREATE [OR REPLACE] FUNCTION function_name
(arg1 [ { IN | OUT | IN OUT }] type1 [DEFAULT value1],
[arg2 [ { IN | OUT | IN OUT }] type2 [DEFAULT value1]],
......
[argn [ { IN | OUT | IN OUT }] typen [DEFAULT valuen]])
[ AUTHID DEFINER | CURRENT_USER ]
RETURN return_type
IS | AS
<类型.变量的声明部分>
BEGIN
执行部分
RETURN expression
EXCEPTION
异常处理部分
END function_name;
l IN,OUT,IN OUT是形参的模式。若省略,则为IN模式。IN模式的形参只能将实参传递给形参,进入函数内部,但只能读不能写,函数返回时实参的值不变。OUT模式的形参会忽略调用时的实参值(或说该形参的初始值总是NULL),但在函数内部可以被读或写,函数返回时形参的值会赋予给实参。IN OUT具有前两种模式的特性,即调用时,实参的值总是传递给形参,结束时,形参的值传递给实参。调用时,对于IN模式的实参可以是常量或变量,但对于OUT和IN OUT模式的实参必须是变量。
l 一般,只有在确认function_name函数是新函数或是要更新的函数时,才使用OR REPALCE关键字,否则容易删除有用的函数。
例1. 获取某部门的工资总和:
--获取某部门的工资总和
SQL> create or replace
2 function f_get_salary(
3 Dept_no NUMBER,
4 Emp_count OUT NUMBER)
5 RETURN NUMBER
6 is
7 V_sum NUMBER;
8 BEGIN
9 SELECT SUM(SALARY), count(*) INTO V_sum, emp_count
10 FROM EMPLOYEES WHERE DEPARTMENT_ID=dept_no;
11 RETURN v_sum;
12 EXCEPTION
13 WHEN NO_DATA_FOUND THEN
14 DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
15 WHEN OTHERS THEN
16 DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
17 END f_get_salary;
18 /
Function created.
- 函数的调用
函数声明时所定义的参数称为形式参数,应用程序调用时为函数传递的参数称为实际参数。应用程序在调用函数时,可以使用以下三种方法向函数传递参数:
第一种参数传递格式:位置表示法。
即在调用时按形参的排列顺序,依次写出实参的名称,而将形参与实参关联起来进行传递。用这种方法进行调用,形参与实参的名称是相互独立,没有关系,强调次序才是重要的。
格式为:
argument_value1[,argument_value2 …]
第二种参数传递格式:名称表示法。
即在调用时按形参的名称与实参的名称,写出实参对应的形参,而将形参与实参关联起来进行传递。这种方法,形参与实参的名称是相互独立的,没有关系,名称的对应关系才是最重要的,次序并不重要。
格式为:
argument => parameter [,…]
其中:argument 为形式参数,它必须与函数定义时所声明的形式参数名称相同parameter 为实际参数。
在这种格式中,形势参数与实际参数成对出现,相互间关系唯一确定,所以参数的顺序可以任意排列。
第三种参数传递格式:组合传递。
即在调用一个函数时,同时使用位置表示法和名称表示法为函数传递参数。采用这种参数传递方法时,使用位置表示法所传递的参数必须放在名称表示法所传递的参数前面。也就是说,无论函数具有多少个参数,只要其中有一个参数使用名称表示法,其后所有的参数都必须使用名称表示法。
SQL> DECLARE
2 V_num NUMBER;
3 V_sum NUMBER;
4 BEGIN
5 V_sum :=f_get_salary(emp_count => v_num, dept_no => 10);
6 DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||v_sum||',人数:'||v_num);
7 end;
8 /
10号部门工资总和:4400,人数:1
PL/SQL procedure successfully completed.
SQL> var V_sum NUMBER;
SQL> var V_num NUMBER;
SQL> exec :V_sum:=f_get_salary(:dept_no,:V_num);
PL/SQL procedure successfully completed.
SQL> print :V_sum :V_num
V_SUM
4400
V_NUM
1
SQL> select :V_sum ,:V_num from dual;
:V_SUM :V_NUM
4400 1
SQL> run
1 begin
2 DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||:v_sum||',人数:'||:v_num);
3* end;
10号部门工资总和:4400,人数:1
SQL> DECLARE
2 V_num NUMBER;
3 V_sum NUMBER;
4 BEGIN
5 V_sum :=f_get_salary(10,emp_count => v_num);
6 DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||v_sum||',人数:'||v_num);
7 end;
8 /
10号部门工资总和:4400,人数:1
PL/SQL procedure successfully completed.
- 参数默认值
在CREATE OR REPLACE FUNCTION 语句中声明函数参数时可以使用DEFAULT关键字为输入参数指定默认值。
例2:
CREATE OR REPLACE FUNCTION f_demo_fun(
Name VARCHAR2,
Age INTEGER,
Sex VARCHAR2 DEFAULT '男')
RETURN VARCHAR2
IS
V_var VARCHAR2(32);
BEGIN
V_var := name||':'||TO_CHAR(age)||'岁,'||sex;
RETURN v_var;
END;
具有默认值的函数创建后,在函数调用时,如果没有为具有默认值的参数提供实际参数值,函数将使用该参数的默认值。但当调用者为默认参数提供实际参数时,函数将使用实际参数值。在创建函数时,只能为输入参数设置默认值,而不能为输入/输出参数设置默认值。
SQL> DECLARE
2 Var VARCHAR(32);
3 BEGIN
4 Var := f_demo_fun('user1', 30);
5 DBMS_OUTPUT.PUT_LINE(var);
6 Var := f_demo_fun('user2', age => 40);
7 DBMS_OUTPUT.PUT_LINE(var);
8 Var := f_demo_fun('user3', sex => '女', age => 20);
9 DBMS_OUTPUT.PUT_LINE(var);
10 END;
11 /
user1:30岁,男
user2:40岁,男
user3:20岁,女
PL/SQL procedure successfully completed.
3 存储过程
3.1 创建过程
建立存储过程
在 ORACLE SERVER上建立存储过程,可以被多个应用程序调用,可以向存储过程传递参数,也可以向存储过程传回参数.
创建过程语法:
CREATE [OR REPLACE] PROCEDURE procedure_name
([arg1 [ IN | OUT | IN OUT ]] type1 [DEFAULT value1],
[arg2 [ IN | OUT | IN OUT ]] type2 [DEFAULT value1]],
......
[argn [ IN | OUT | IN OUT ]] typen [DEFAULT valuen])
[ AUTHID DEFINER | CURRENT_USER ]
{ IS | AS }
<声明部分>
BEGIN
<执行部分>
EXCEPTION
<可选的异常错误处理程序>
END procedure_name;
说明:相关参数说明参见函数的语法说明。
例3.删除指定员工记录;
CREATE OR REPLACE PROCEDURE P_DelEmployees
(v_empno IN employees.employee_id%TYPE)
AS
No_result EXCEPTION;
BEGIN
DELETE FROM employees WHERE employee_id = v_empno;
IF SQL%NOTFOUND THEN
RAISE no_result;
END IF;
DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'的员工已被删除!');
EXCEPTION
WHEN no_result THEN
DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END P_DelEmployees;
/
3.2 调用存储过程
存储过程建立完成后,只要通过授权,用户就可以在SQLPLUS 、ORACLE开发工具或第三方开发工具中来调用运行。对于参数的传递也有三种:按位置传递、按名称传递和组合传递,传递方法与函数的一样。ORACLE 使用EXECUTE 语句来实现对存储过程的调用:
EXEC[UTE] procedure_name( parameter1, parameter2…);
SQL> exec P_DelEmployees(300);
温馨提示:你需要的数据不存在!
PL/SQL procedure successfully completed.
SQL> declare
2 v_Employeesid number;
3 begin
4 v_Employeesid:=300;
5 P_DelEmployees(v_Employeesid);
6 end;
7 /
温馨提示:你需要的数据不存在!
PL/SQL procedure successfully completed.
3.3 AUTHID
过程中的AUTHID 指令可以告诉ORACLE ,这个过程使用谁的权限运行.默任情况下,存储过程会作为调用者的过程运行,但是具有设计者的特权.这称为设计者权利运行.
与过程相关数据字典
USER_SOURCE, ALL_SOURCE, DBA_SOURCE, USER_ERRORS,
ALL_PROCEDURES,USER_OBJECTS,ALL_OBJECTS,DBA_OBJECTS
相关的权限:
CREATE ANY PROCEDURE
DROP ANY PROCEDURE
在SQL*PLUS 中,可以用DESCRIBE 命令查看过程的名字及其参数表。
DESC[RIBE] Procedure_name;
删除过程和函数
1.删除过程
可以使用DROP PROCEDURE命令对不需要的过程进行删除,语法如下:
DROP PROCEDURE [user.]Procudure_name;
2.删除函数
可以使用DROP FUNCTION 命令对不需要的函数进行删除,语法如下:
DROP FUNCTION [user.]Function_name;
使用过程与函数具有如下优点:
1、共同使用的代码可以只需要被编写和测试一次,而被需要该代码的任何应用程序(如:.NET、C++、JAVA、VB程序,也可以是DLL库)调用。
2、这种集中编写、集中维护更新、大家共享(或重用)的方法,简化了应用程序的开发和维护,提高了效率与性能。
3、这种模块化的方法,使得可以将一个复杂的问题、大的程序逐步简化成几个简单的、小的程序部分,进行分别编写、调试。因此使程序的结构清晰、简单,也容易实现。
4、可以在各个开发者之间提供处理数据、控制流程、提示信息等方面的一致性。
5、节省内存空间。它们以一种压缩的形式被存储在外存中,当被调用时才被放入内存进行处理。并且,如果多个用户要执行相同的过程或函数时,就只需要在内存中加载一个该过程或函数。
6、提高数据的安全性与完整性。通过把一些对数据的操作放到过程或函数中,就可以通过是否授予用户有执行该过程或的权限,来限制某些用户对数据进行这些操作。
过程与函数的相同功能有:
1、 都使用IN模式的参数传入数据、OUT模式的参数返回数据。
2、 输入参数都可以接受默认值,都可以传值或传引导。
3、 调用时的实际参数都可以使用位置表示法、名称表示法或组合方法。
4、 都有声明部分、执行部分和异常处理部分。
5、 其管理过程都有创建、编译、授权、删除、显示依赖关系等。
使用过程与函数的原则:
1、如果需要返回多个值和不返回值,就使用过程;如果只需要返回一个值,就使用函数。
2、过程一般用于执行一个指定的动作,函数一般用于计算和返回一个值。
3、可以SQL语句内部(如表达式)调用函数来完成复杂的计算问题,但不能调用过程。所以这是函数的特色。