过程与函数
过程和函数统称为PL/SQL子程序,他们是被命名的PL/SQL块,被编译后存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。
一 创建函数
1 创建函数
语法:
1 CREATE [OR REPLACE] FUNCTION function_name
2 (arg1 [ { IN | OUT | IN OUT }] type1 [DEFAULT value1],
3 [arg2 [ { IN | OUT | IN OUT }] type2 [DEFAULT value1]],
4 ......
5 [argn [ { IN | OUT | IN OUT }] typen [DEFAULT valuen]])
6 [ AUTHID DEFINER | CURRENT_USER ]
7 RETURN return_type
8 IS | AS
9 <类型.变量的声明部分>
10 BEGIN
11 执行部分
12 RETURN expression
13 EXCEPTION
14 异常处理部分
15 END function_name;
i. IN,OUT,IN OUT是形参的模式。若省略,则为IN模式。IN模式的形参只能将实参传递给形参,进入函数内部,但只能读不能写,函数返回时实参的值不变。OUT模式的形参会忽略调用时的实参值(或说该形参的初始值总是NULL),但在函数内部可以被读或写,函数返回时形参的值会赋予给实参。IN OUT具有前两种模式的特性,即调用时,实参的值总是传递给形参,结束时,形参的值传递给实参。调用时,对于IN模式的实参可以是常量或变量,但对于OUT和IN OUT模式的实参必须是变量。
ii. 一般,只有在确认function_name函数是新函数或是要更新的函数时,才使用OR REPALCE关键字,否则容易删除有用的函数。
例子: 获取某部门的工资总和
1 --获取某部门的工资总和
2 CREATE OR REPLACE
3 FUNCTION get_salary(
4 Dept_no NUMBER,
5 Emp_count OUT NUMBER)
6 RETURN NUMBER
7 IS
8 V_sum NUMBER;
9 BEGIN
10 SELECT SUM(SALARY), count(*) INTO V_sum, emp_count
11 FROM EMPLOYEES WHERE DEPARTMENT_ID=dept_no;
12 RETURN v_sum;
13 EXCEPTION
14 WHEN NO_DATA_FOUND THEN
15 DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
16 WHEN OTHERS THEN
17 DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
18 END get_salary;
2 函数的调用
函数声明时所定义的参数称为形式参数,应用程序调用时为函数传递的参数称为实际参数。应用程序在调用函数时,可以使用以下三种方法向函数传递参数:
第一种参数传递格式:位置表示法
即在调用时按形参的排列顺序,依次写出实参的名称,而将形参与实参关联起来进行传递。用这种方法进行调用,形参与实参的名称是相互独立,没有关系,强调次序才是重要的。格式为:
argument_value1[,argument_value2 …]
例子:计算某部门的工资总和
1 DECLARE
2 V_num NUMBER;
3 V_sum NUMBER;
4 BEGIN
5 V_sum :=get_salary(10, v_num);
6 DBMS_OUTPUT.PUT_LINE('部门号为:10的工资总和:'||v_sum||',人数为:'||v_num);
7 END;
第二种参数传递格式:名称表示法
即在调用时按形参的名称与实参的名称,写出实参对应的形参,而将形参与实参关联起来进行传递。这种方法,形参与实参的名称是相互独立的,没有关系,名称的对应关系才是最重要的,次序并不重要。格式为:
argument => parameter [,…]
其中:argument为形式参数,它必须与函数定义时所声明的形式参数名称相同parameter为实际参数。
在这种格式中,形势参数与实际参数成对出现,相互间关系唯一确定,所以参数的顺序可以任意排列。
例子:计算某部门的工资总和
1 DECLARE
2 V_num NUMBER;
3 V_sum NUMBER;
4 BEGIN
5 V_sum :=get_salary(emp_count => v_num, dept_no => 10);
6 DBMS_OUTPUT.PUT_LINE('部门号为:10的工资总和:'||v_sum||',人数为:'||v_num);
7 END;
第三种参数传递格式:组合传递
即在调用一个函数时,同时使用位置表示法和名称表示法为函数传递参数。采用这种参数传递方法时,使用位置表示法所传递的参数必须放在名称表示法所传递的参数前面。也就是说,无论函数具有多少个参数,只要其中有一个参数使用名称表示法,其后所有的参数都必须使用名称表示法。
例子:
1 CREATE OR REPLACE FUNCTION demo_fun(
2 Name VARCHAR2, --注意VARCHAR2不能给精度,如:VARCHAR2(10),其它类似
3 Age INTEGER,
4 Sex VARCHAR2)
5 RETURN VARCHAR2
6 AS
7 V_var VARCHAR2(32);
8 BEGIN
9 V_var := name||':'||TO_CHAR(age)||'岁.'||sex;
10 RETURN v_var;
11 END;
12
13 DECLARE
14 Var VARCHAR(32);
15 BEGIN
16 Var := demo_fun('user1', 30, sex => '男');
17 DBMS_OUTPUT.PUT_LINE(var);
18
19 Var := demo_fun('user2', age => 40, sex => '男');
20 DBMS_OUTPUT.PUT_LINE(var);
21
22 Var := demo_fun('user3', sex => '女', age => 20);
23 DBMS_OUTPUT.PUT_LINE(var);
24 END;
无论采用哪一种参数传递方法,实际参数和形式参数之间的数据传递只有两种方法:传址法和传值法。所谓传址法是指在调用函数时,将实际参数的地址指针传递给形式参数,使形式参数和实际参数指向内存中的同一区域,从而实现参数数据的传递。这种方法又称作参照法,即形式参数参照实际参数数据。输入参数均采用传址法传递数据。
传值法是指将实际参数的数据拷贝到形式参数,而不是传递实际参数的地址。默认时,输出参数和输入/输出参数均采用传值法。在函数调用时,ORACLE将实际参数数据拷贝到输入/输出参数,而当函数正常运行退出时,又将输出形式参数和输入/输出形式参数数据拷贝到实际参数变量中。
3 参数默认值
在CREATE OR REPLACE FUNCTION语句中声明函数参数时可以使用 DEFAULT 关键字为输入参数指定默认值。
实例:
1 CREATE OR REPLACE FUNCTION demo_fun(
2 Name VARCHAR2,
3 Age INTEGER,
4 Sex VARCHAR2 DEFAULT '男')
5 RETURN VARCHAR2
6 AS
7 V_var VARCHAR2(32);
8 BEGIN
9 V_var := name||':'||TO_CHAR(age)||'岁.'||sex;
10 RETURN v_var;
11 END;
具有默认值的函数创建后,在函数调用时,如果没有为具有默认值的参数提供实际参数值,函数将使用该参数的默认值。但当调用者为默认参数提供实际参数时,函数将使用实际参数值。在创建函数时,只能为输入参数设置默认值,而不能为输入/输出参数设置默认值。
1 DECLARE
2 var VARCHAR(32);
3 BEGIN
4 Var := demo_fun('user1', 30);
5 DBMS_OUTPUT.PUT_LINE(var);
6 Var := demo_fun('user2', age => 40);
7 DBMS_OUTPUT.PUT_LINE(var);
8 Var := demo_fun('user3', sex => '女', age => 20);
9 DBMS_OUTPUT.PUT_LINE(var);
10 END;
二 存储过程
1 创建过程
在ORACLE SERVER上建立存储过程,可以被多个应用程序调用,可以向存储过程传递参数,也可以向存储过程传回参数.
创建过程语法:
1 CREATE [OR REPLACE] PROCEDURE procedure_name
2 ([arg1 [ IN | OUT | IN OUT ]] type1 [DEFAULT value1],
3 [arg2 [ IN | OUT | IN OUT ]] type2 [DEFAULT value1]],
4 ......
5 [argn [ IN | OUT | IN OUT ]] typen [DEFAULT valuen])
6 [ AUTHID DEFINER | CURRENT_USER ]
7 { IS | AS }
8 <声明部分>
9 BEGIN
10 <执行部分>
11 EXCEPTION
12 <可选的异常错误处理程序>
13 END procedure_name;
实例:
用户连接登记记录
1 CREATE TABLE logtable (userid VARCHAR2(10), logdate date);
2
3 CREATE OR REPLACE PROCEDURE logexecution
4 IS
5 BEGIN
6 INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
7 END;
删除指定员工记录
1 CREATE OR REPLACE
2 PROCEDURE DelEmp
3 (v_empno IN employees.employee_id%TYPE)
4 AS
5 No_result EXCEPTION;
6 BEGIN
7 DELETE FROM employees WHERE employee_id = v_empno;
8 IF SQL%NOTFOUND THEN
9 RAISE no_result;
10 END IF;
11 DBMS_OUTPUT.PUT_LINE('编码为'||v_empno||'的员工已被删除!');
12 EXCEPTION
13 WHEN no_result THEN
14 DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!');
15 WHEN OTHERS THEN
16 DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
17 END DelEmp;
插入员工记录
1 CREATE OR REPLACE
2 PROCEDURE InsertEmp(
3 v_empno in employees.employee_id%TYPE,
4 v_firstname in employees.first_name%TYPE,
5 v_lastname in employees.last_name%TYPE,
6 v_deptno in employees.department_id%TYPE
7 )
8 AS
9 empno_remaining EXCEPTION;
10 PRAGMA EXCEPTION_INIT(empno_remaining, -1);
11 /* -1 是违反唯一约束条件的错误代码 */
12 BEGIN
13 INSERT INTO EMPLOYEES(EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE,DEPARTMENT_ID)
14 VALUES(v_empno, v_firstname,v_lastname, sysdate, v_deptno);
15 DBMS_OUTPUT.PUT_LINE('温馨提示:插入数据记录成功!');
16 EXCEPTION
17 WHEN empno_remaining THEN
18 DBMS_OUTPUT.PUT_LINE('温馨提示:违反数据完整性约束!');
19 WHEN OTHERS THEN
20 DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
21 END InsertEmp;
使用存储过程向departments表中插入数据
1 CREATE OR REPLACE
2 PROCEDURE insert_dept
3 (v_dept_id IN departments.department_id%TYPE,
4 v_dept_name IN departments.department_name%TYPE,
5 v_mgr_id IN departments.manager_id%TYPE,
6 v_loc_id IN departments.location_id%TYPE)
7 IS
8 ept_null_error EXCEPTION; -- 用户定义的异常处理
9 PRAGMA EXCEPTION_INIT(ept_null_error, -1400);
10 ept_no_loc_id EXCEPTION; -- 用户定义的异常处理
11 PRAGMA EXCEPTION_INIT(ept_no_loc_id, -2291);
12 BEGIN
13 INSERT INTO departments
14 (department_id, department_name, manager_id, location_id)
15 VALUES
16 (v_dept_id, v_dept_name, v_mgr_id, v_loc_id);
17 DBMS_OUTPUT.PUT_LINE('插入部门'||v_dept_id||'成功');
18 EXCEPTION
19 WHEN DUP_VAL_ON_INDEX THEN
20 RAISE_APPLICATION_ERROR(-20000, '部门编码不能重复');
21 WHEN ept_null_error THEN
22 RAISE_APPLICATION_ERROR(-20001, '部门编码、部门名称不能为空');
23 WHEN ept_no_loc_id THEN
24 RAISE_APPLICATION_ERROR(-20002, '没有该地点');
25 END insert_dept;
26
27 -- 调用实例:
28 DECLARE
29 ept_20000 EXCEPTION;
30 PRAGMA EXCEPTION_INIT(ept_20000, -20000);
31 ept_20001 EXCEPTION;
32 PRAGMA EXCEPTION_INIT(ept_20001, -20001);
33 ept_20002 EXCEPTION;
34 PRAGMA EXCEPTION_INIT(ept_20002, -20002);
35 BEGIN
36 insert_dept(300, '部门300', 100, 2400);
37 insert_dept(310, NULL, 100, 2400);
38 insert_dept(310, '部门310', 100, 900);
39
40 /*
41 insert_dept(v_dept_name => '部门310', v_dept_id => 310,
42 v_mgr_id => 100, v_loc_id => 2400);
43 insert_dept(320, '部门320', v_mgr_id => 100, v_loc_id => 900);
44 */
45 EXCEPTION
46 WHEN ept_20000 THEN
47 DBMS_OUTPUT.PUT_LINE('ept_20000部门编码不能重复');
48 WHEN ept_20001 THEN
49 DBMS_OUTPUT.PUT_LINE('ept_20001部门编码、部门名称不能为空');
50 WHEN ept_20002 THEN
51 DBMS_OUTPUT.PUT_LINE('ept_20002没有该地点');
52 WHEN OTHERS THEN
53 DBMS_OUTPUT.PUT_LINE('others出现了其他异常错误');
54 END;
2 调用存储过程
存储过程建立完成后,只要通过授权,用户就可以在SQLPLUS 、ORACLE开发工具或第三方开发工具中来调用运行。对于参数的传递也有三种:按位置传递、按名称传递和组合传递,传递方法与函数的一样。ORACLE 使用 EXECUTE 语句来实现对存储过程的调用:
EXEC[UTE] procedure_name( parameter1, parameter2…);
比如:
EXECUTE logexecution;
实例:
查询指定员工记录
1 CREATE OR REPLACE 2 PROCEDURE QueryEmp 3 (v_empno IN employees.employee_id%TYPE, 4 v_ename OUT employees.first_name%TYPE, 5 v_sal OUT employees.salary%TYPE) 6 AS 7 BEGIN 8 SELECT last_name || last_name, salary INTO v_ename, v_sal 9 FROM employees 10 WHERE employee_id = v_empno; 11 DBMS_OUTPUT.PUT_LINE('温馨提示:编码为'||v_empno||'的员工已经查到!'); 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 QueryEmp;
18 --调用 19 DECLARE 20 v1 employees.first_name%TYPE; 21 v2 employees.salary%TYPE; 22 BEGIN 23 QueryEmp(100, v1, v2); 24 DBMS_OUTPUT.PUT_LINE('姓名:'||v1); 25 DBMS_OUTPUT.PUT_LINE('工资:'||v2); 26 QueryEmp(103, v1, v2); 27 DBMS_OUTPUT.PUT_LINE('姓名:'||v1); 28 DBMS_OUTPUT.PUT_LINE('工资:'||v2); 29 QueryEmp(104, v1, v2); 30 DBMS_OUTPUT.PUT_LINE('姓名:'||v1); 31 DBMS_OUTPUT.PUT_LINE('工资:'||v2); 32 END;
计算指定部门的工资总和,并统计其中的职工数量
1 CREATE OR REPLACE
2 PROCEDURE proc_demo
3 (
4 dept_no NUMBER DEFAULT 10,
5 sal_sum OUT NUMBER,
6 emp_count OUT NUMBER
7 )
8 IS
9 BEGIN
10 SELECT SUM(salary), COUNT(*) INTO sal_sum, emp_count
11 FROM employees WHERE department_id = dept_no;
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 proc_demo;
18
19 DECLARE
20 V_num NUMBER;
21 V_sum NUMBER(8, 2);
22 BEGIN
23 Proc_demo(30, v_sum, v_num);
24 DBMS_OUTPUT.PUT_LINE('温馨提示:30号部门工资总和:'||v_sum||',人数:'||v_num);
25 Proc_demo(sal_sum => v_sum, emp_count => v_num);
26 DBMS_OUTPUT.PUT_LINE('温馨提示:10号部门工资总和:'||v_sum||',人数:'||v_num);
27 END;
在PL/SQL 程序中还可以在块内建立本地函数和过程,这些函数和过程不存储在数据库中,但可以在创建它们的PL/SQL 程序中被重复调用。本地函数和过程在PL/SQL 块的声明部分定义,它们的语法格式与存储函数和过程相同,但不能使用CREATE OR REPLACE 关键字。
实例:
建立本地过程,用于计算指定部门的工资总和,并统计其中的职工数量
1 DECLARE
2 V_num NUMBER;
3 V_sum NUMBER(8, 2);
4 PROCEDURE proc_demo
5 (
6 Dept_no NUMBER DEFAULT 10,
7 Sal_sum OUT NUMBER,
8 Emp_count OUT NUMBER
9 )
10 IS
11 BEGIN
12 SELECT SUM(salary), COUNT(*) INTO sal_sum, emp_count
13 FROM employees WHERE department_id=dept_no;
14 EXCEPTION
15 WHEN NO_DATA_FOUND THEN
16 DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
17 WHEN OTHERS THEN
18 DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
19 END proc_demo;
20 --调用方法:
21 BEGIN
22 Proc_demo(30, v_sum, v_num);
23 DBMS_OUTPUT.PUT_LINE('30号部门工资总和:'||v_sum||',人数:'||v_num);
24 Proc_demo(sal_sum => v_sum, emp_count => v_num);
25 DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||v_sum||',人数:'||v_num);
26 END;
3 AUTHID
过程中的AUTHID指令可以告诉ORACLE,这个过程使用谁的权限运行。默任情况下,存储过程会作为调用者的过程运行,但是具有设计者的特权。这称为设计者权利运行。
实例:
建立过程,使用AUTOID DEFINER
1 Connect HR/qaz
2 DROP TABLE logtable;
3 CREATE table logtable (userid VARCHAR2(10), logdate date);
4
5 CREATE OR REPLACE PROCEDURE logexecution
6 AUTHID DEFINER
7 IS
8 BEGIN
9 INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
10 END;
11
12 GRANT EXECUTE ON logexecution TO PUBLIC;
13
14 CONNECT / AS SYSDBA
15 GRANT CONNECT TO testuser1 IDENTIFIED BY userpwd1;
16
17 CONNECT testuser1/userpwd1
18 INSERT INTO HR.LOGTABLE VALUES (USER, SYSDATE);
19 EXECUTE HR.logexecution
20
21 CONNECT HR/qaz
22 SELECT * FROM HR.logtable;
建立过程,使用AUTOID CURRENT_USER
1 CONNECT HR/qaz
2
3 CREATE OR REPLACE PROCEDURE logexecution
4 AUTHID CURRENT_USER
5 IS
6 BEGIN
7 INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
8 END;
9
10 GRANT EXECUTE ON logexecution TO PUBLIC;
11
12 CONNECT testuser1/userpwd1
13 INSERT INTO HR.LOGTABLE VALUES (USER, SYSDATE);
14 EXECUTE HR.logexecution
4 PRAGMA AUTONOMOUS_TRANSACTION(自治事务)
在Oracle中,一个事务是从执行第一个数据管理语言(DML)语句开始,直到执行一个COMMIT语句,提交保存这个事务,或者执行一个ROLLBACK语句,放弃此次操作结束。事务的“要么全部完成,要么什么都没完成”的本性会使将错误信息记入数据库表中变得很困难,因为当事务失败重新运行时,用来编写日志条目的INSERT语句还未完成。
针对这种困境,Oracle提供了一种便捷的方法,即自治事务。自治事务从当前事务开始,在其自身的语境中执行。它们能独立地被提交或重新运行,而不影响正在运行的事务。正因为这样,它们成了编写错误日志表格的理想形式。在事务中检测到错误时,您可以在错误日志表格中插入一行并提交它,然后在不丢失这次插入的情况下回滚主事务。
因为自治事务是与主事务相分离的,所以它不能检测到被修改过的行的当前状态。这就好像在主事务提交之前,它们一直处于单独的会话里,对自治事务来说,它们是不可用的。然而,反过来情况就不同了:主事务能够检测到已经执行过的自治事务的结果。
要创建一个自治事务,您必须在匿名块的最高层或者存储过程、函数、数据包或触发的定义部分中,使用PL/SQL中的PRAGMA AUTONOMOUS_TRANSACTION语句。在这样的模块或过程中执行的SQL Server语句都是自治的。
实例:
建立过程,使用自动事务处理进行日志记录
1 DROP TABLE logtable;
2
3 CREATE TABLE logtable(
4 Username varchar2(20),
5 Dassate_time date,
6 Mege varchar2(60)
7 );
8
9 CREATE TABLE temp_table( N number );
10
11 CREATE OR REPLACE PROCEDURE log_message(p_message varchar2)
12 AS
13 PRAGMA AUTONOMOUS_TRANSACTION;
14 BEGIN
15 INSERT INTO logtable VALUES ( user, sysdate, p_message );
16 COMMIT;
17 END log_message;
18
19 BEGIN
20 Log_message (‘About to insert into temp_table‘);
21 INSERT INTO temp_table VALUES (1);
22 Log_message (‘Rollback to insert into temp_table‘);
23 ROLLBACK;
24 END;
25
26 SELECT * FROM logtable;
27 SELECT * FROM temp_table;
建立过程,没有使用自动事务处理进行日志记录
1 CREATE OR REPLACE PROCEDURE log_message(p_message varchar2)
2 AS
3 BEGIN
4 INSERT INTO logtable VALUES ( user, sysdate, p_message );
5 COMMIT;
6 END log_message;
7
8 BEGIN
9 Log_message ('About to insert into temp_table');
10 INSERT INTO temp_table VALUES (1);
11 Log_message ('Rollback to insert into temp_table');
12 ROLLBACK;
13 END;
14
15 SELECT * FROM logtable;
16 SELECT * FROM temp_table;
5 删除过程和函数
删除过程:可以使用DROP PROCEDURE命令对不需要的过程进行删除,语法如下:
DROP PROCEDURE [user.]Procudure_name;
删除函数:可以使用DROP FUNCTION 命令对不需要的函数进行删除,语法如下:
1 DROP FUNCTION [user.]Function_name;
2
3 --删除上面实例创建的存储过程与函数
4 DROP PROCEDURE logexecution;
5 DROP PROCEDURE delemp;
6 DROP PROCEDURE insertemp;
7 DROP PROCEDURE fireemp;
8 DROP PROCEDURE queryemp;
9 DROP PROCEDURE proc_demo;
10 DROP PROCEDURE log_message;
11 DROP FUNCTION demo_fun;
12 DROP FUNCTION get_salary;
6 过程与函数的比较
使用过程与函数具有如下优点:
1、共同使用的代码可以只需要被编写和测试一次,而被需要该代码的任何应用程序(如:.NET、C++、JAVA、VB程序,也可以是DLL库)调用。
2、这种集中编写、集中维护更新、大家共享(或重用)的方法,简化了应用程序的开发和维护,提高了效率与性能。
3、这种模块化的方法,使得可以将一个复杂的问题、大的程序逐步简化成几个简单的、小的程序部分,进行分别编写、调试。因此使程序的结构清晰、简单,也容易实现。
4、可以在各个开发者之间提供处理数据、控制流程、提示信息等方面的一致性。
5、节省内存空间。它们以一种压缩的形式被存储在外存中,当被调用时才被放入内存进行处理。并且,如果多个用户要执行相同的过程或函数时,就只需要在内存中加载一个该过程或函数。
6、提高数据的安全性与完整性。通过把一些对数据的操作放到过程或函数中,就可以通过是否授予用户有执行该过程或的权限,来限制某些用户对数据进行这些操作。
过程与函数的相同功能有:
1、 都使用IN模式的参数传入数据、OUT模式的参数返回数据。
2、 输入参数都可以接受默认值,都可以传值或传引导。
3、 调用时的实际参数都可以使用位置表示法、名称表示法或组合方法。
4、 都有声明部分、执行部分和异常处理部分。
5、 其管理过程都有创建、编译、授权、删除、显示依赖关系等。
使用过程与函数的原则:
1、如果需要返回多个值和不返回值,就使用过程;如果只需要返回一个值,就使用函数。
2、过程一般用于执行一个指定的动作,函数一般用于计算和返回一个值。
3、可以SQL语句内部(如表达式)调用函数来完成复杂的计算问题,但不能调用过程。所以这是函数的特色。