Oracle——10用户自定义函数

Oracle——10用户自定义函数
Oraclefunction函数自定义调用 
 

用户自定义函数是存储在数据库中的代码块,可以把值返回到调用程序。函数的参数有3种类型:
(1)in参数类型:表示输入给函数的参数,该参数只能用于传值,不能被赋值。
(2)out参数类型:表示参数在函数中被赋值,可以传给函数调用程序,该参数只能用于赋值,不能用于传值。
(3)in out参数类型:表示参数既可以传值,也可以被赋值。
 
1.函数的创建
语法格式:
 
Sql代码  
    create [or replace] function functionName  
    (  
        parameterName1 mode1 dataType1,  
        parameterName2 mode2 dataType2,  
        ...  
    )  
    return returnDataType  
    is/as  
    begin  
        function_body  
        return expression  
    end functionName; -- 结束函数的声明,也可以直接写end不加函数名。  
--其中mode1、mode2表示参数类型,dataType表示参数的数据类型。returnDataType表示返回值类型。  
 
示例1:
 
Sql代码  
create or replace function explainParameter -- 定义一个名为explainParameter的函数  
(  
    inParam in char,    --  定义该参数类型为in参数类型,只能用于赋值  
    outParam out char,  --  out参数类型,只能用于传值  
    inAndOutParam in out char   --  in out参数类型,既能赋值,又能传值  
)  
return char     --  表示函数的返回类型为char类型  
as      -- 表示函数体部分  
    returnChar char;        --  声明零时变量,这是可有可无的,这里声明返回变量为char类型的returnChar变量。  
begin  
    inParam := 'Hello World';   -- 这是错误的,in类型的参数只能用来传值,不能赋值  
    outParam := 'Hello World'; -- 这是正确的,out类型的参数可以用来被赋值  
    inAndOutParam := 'Hello World'; -- 这是正确的,in out参数既可以用来传值,又可以被赋值  
    inAndOutParam := outParam; -- 这是错误的,out参数不能用来传值  
    return(returnChar); -- 返回returnChar,也可以不要括号,直接写return returnChar。  
end explainParameter; -- 结束explainParameter函数,表示explainParameter函数声明结束,也可以直接写end,不加函数名。  
 
 
示例2:
 
Sql代码  
CREATE OR REPLACE   
FUNCTION testFunc (num1 IN NUMBER, num2 IN NUMBER)  
RETURN NUMBER  
AS  
    num3 number;  
    num4 number;  
    num5 number;  
BEGIN  
    num3 := num1 + num2;  
    num4 := num1 * num2;  
    num5 := num3 * num4;  
    RETURN num5;  
END;  
 
 
 
2.函数的调用
自定义函数的调用方法跟系统内置函数的调用方法相同,可以直接在select语句中调用,也可以在函数中调用,如下:
select testFunc(1,2) from tableName;
num := testFunc(1,2);
 
 
3.函数的删除
自定义函数的删除方法类似于表的删除,语法格式如下:
drop function [schema.]functionName;

.1 引言
6.2 创建函数
6.3 存储过程
6.3.1 创建过程
6.3.2 调用存储过程
6.3.3 AUTHID
6.3.4 PRAGMA AUTONOMOUS_TRANSACTION
6.3.5 开发存储过程步骤
6.3.6 删除过程和函数
6.3.7 过程与函数的比较
 
6.1 引言
过程与函数(另外还有包与触发器)是命名的PL/SQL块(也是用户的方案对象),被编译后存储在数据库中,以备执行。因此,其它PL/SQL块可以按名称来使用他们。所以,可以将商业逻辑、企业规则写成函数或过程保存到数据库中,以便共享。
过程和函数统称为PL/SQL子程序,他们是被命名的PL/SQL块,均存储在数据库中,并通过输入、输出参数或输入/输出参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程则不返回数据。在本节中,主要介绍:
1.   创建存储过程和函数。
2.   正确使用系统级的异常处理和用户定义的异常处理。
3.   建立和管理存储过程和函数。
6.2 创建函数
1. 创建函数
 
语法如下:
 

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.           获取某部门的工资总和:
 

--获取某部门的工资总和
CREATE OR REPLACE
FUNCTION get_salary(
  Dept_no NUMBER,
  Emp_count OUT NUMBER)
  RETURN NUMBER 
IS
  V_sum NUMBER;
BEGIN
  SELECT SUM(SALARY), count(*) INTO V_sum, emp_count
    FROM EMPLOYEES WHERE DEPARTMENT_ID=dept_no;
  RETURN v_sum;
EXCEPTION
   WHEN NO_DATA_FOUND THEN 
      DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
   WHEN OTHERS THEN 
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END get_salary;

 
2. 函数的调用
函数声明时所定义的参数称为形式参数,应用程序调用时为函数传递的参数称为实际参数。应用程序在调用函数时,可以使用以下三种方法向函数传递参数:
 
第一种参数传递格式:位置表示法。
即在调用时按形参的排列顺序,依次写出实参的名称,而将形参与实参关联起来进行传递。用这种方法进行调用,形参与实参的名称是相互独立,没有关系,强调次序才是重要的。
格式为:
       argument_value1[,argument_value2 …]
 
例2:计算某部门的工资总和:
 

DECLARE
  V_num NUMBER;
  V_sum NUMBER;
BEGIN
  V_sum :=get_salary(10, v_num);
  DBMS_OUTPUT.PUT_LINE('部门号为:10的工资总和:'||v_sum||',人数为:'||v_num);
END;

第二种参数传递格式:名称表示法。
即在调用时按形参的名称与实参的名称,写出实参对应的形参,而将形参与实参关联起来进行传递。这种方法,形参与实参的名称是相互独立的,没有关系,名称的对应关系才是最重要的,次序并不重要。
格式为:
       argument => parameter [,…]
其中:argument 为形式参数,它必须与函数定义时所声明的形式参数名称相同parameter 为实际参数。
在这种格式中,形势参数与实际参数成对出现,相互间关系唯一确定,所以参数的顺序可以任意排列。
例3:计算某部门的工资总和:
 

DECLARE
  V_num NUMBER;
    V_sum NUMBER;
BEGIN
    V_sum :=get_salary(emp_count => v_num, dept_no => 10);
    DBMS_OUTPUT.PUT_LINE('部门号为:10的工资总和:'||v_sum||',人数为:'||v_num);
END;
 

第三种参数传递格式:组合传递。
即在调用一个函数时,同时使用位置表示法和名称表示法为函数传递参数。采用这种参数传递方法时,使用位置表示法所传递的参数必须放在名称表示法所传递的参数前面。也就是说,无论函数具有多少个参数,只要其中有一个参数使用名称表示法,其后所有的参数都必须使用名称表示法。
 
例4:

CREATE OR REPLACE FUNCTION demo_fun(
  Name VARCHAR2,--注意VARCHAR2不能给精度,如:VARCHAR2(10),其它类似
  Age INTEGER,
  Sex VARCHAR2)
  RETURN VARCHAR2 
AS
  V_var VARCHAR2(32);
BEGIN
  V_var := name||':'||TO_CHAR(age)||'岁.'||sex;
  RETURN v_var;
END;

DECLARE 
  Var VARCHAR(32);
BEGIN
  Var := demo_fun('user1', 30, sex => '男');
  DBMS_OUTPUT.PUT_LINE(var);

  Var := demo_fun('user2', age => 40, sex => '男');
  DBMS_OUTPUT.PUT_LINE(var);

  Var := demo_fun('user3', sex => '女', age => 20);
  DBMS_OUTPUT.PUT_LINE(var);
END;

 
无论采用哪一种参数传递方法,实际参数和形式参数之间的数据传递只有两种方法:传址法和传值法。所谓传址法是指在调用函数时,将实际参数的地址指针传递给形式参数,使形式参数和实际参数指向内存中的同一区域,从而实现参数数据的传递。这种方法又称作参照法,即形式参数参照实际参数数据。输入参数均采用传址法传递数据。
       传值法是指将实际参数的数据拷贝到形式参数,而不是传递实际参数的地址。默认时,输出参数和输入/输出参数均采用传值法。在函数调用时,ORACLE将实际参数数据拷贝到输入/输出参数,而当函数正常运行退出时,又将输出形式参数和输入/输出形式参数数据拷贝到实际参数变量中。
 
3. 参数默认值
在CREATE OR REPLACE FUNCTION 语句中声明函数参数时可以使用DEFAULT关键字为输入参数指定默认值。
 
例5:

CREATE OR REPLACE FUNCTION demo_fun(
  Name VARCHAR2,
  Age INTEGER,
  Sex VARCHAR2 DEFAULT '男')
  RETURN VARCHAR2 
AS
  V_var VARCHAR2(32);
BEGIN
  V_var := name||':'||TO_CHAR(age)||'岁.'||sex;
  RETURN v_var;
END;

 
具有默认值的函数创建后,在函数调用时,如果没有为具有默认值的参数提供实际参数值,函数将使用该参数的默认值。但当调用者为默认参数提供实际参数时,函数将使用实际参数值。在创建函数时,只能为输入参数设置默认值,而不能为输入/输出参数设置默认值。
DECLARE
 var VARCHAR(32);
BEGIN
 Var := demo_fun('user1', 30);
 DBMS_OUTPUT.PUT_LINE(var);
 Var := demo_fun('user2', age => 40);
 DBMS_OUTPUT.PUT_LINE(var);
 Var := demo_fun('user3', sex => '女', age => 20);
 DBMS_OUTPUT.PUT_LINE(var);
END;
6.3 存储过程
6.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;

 
说明:相关参数说明参见函数的语法说明。
 
例6.用户连接登记记录;
 

CREATE TABLE logtable (userid VARCHAR2(10), logdate date);

CREATE OR REPLACE PROCEDURE logexecution 
IS
BEGIN
INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
END;

 
例7.删除指定员工记录;
 

CREATE OR REPLACE
PROCEDURE DelEmp
(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 DelEmp;

 
例8.插入员工记录:
 

CREATE OR REPLACE
PROCEDURE InsertEmp(
   v_empno     in employees.employee_id%TYPE,
   v_firstname in employees.first_name%TYPE,
   v_lastname  in employees.last_name%TYPE,
   v_deptno    in employees.department_id%TYPE
   ) 
AS
   empno_remaining EXCEPTION;
   PRAGMA EXCEPTION_INIT(empno_remaining, -1);
   /* -1 是违反唯一约束条件的错误代码 */
BEGIN
   INSERT INTO EMPLOYEES(EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE,DEPARTMENT_ID)
   VALUES(v_empno, v_firstname,v_lastname, sysdate, v_deptno);
   DBMS_OUTPUT.PUT_LINE('温馨提示:插入数据记录成功!');
EXCEPTION
   WHEN empno_remaining THEN 
      DBMS_OUTPUT.PUT_LINE('温馨提示:违反数据完整性约束!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END InsertEmp;

例9.使用存储过程向departments表中插入数据。
 

CREATE OR REPLACE
PROCEDURE insert_dept
  (v_dept_id IN departments.department_id%TYPE,
   v_dept_name IN departments.department_name%TYPE,
   v_mgr_id IN departments.manager_id%TYPE,
   v_loc_id IN departments.location_id%TYPE)
IS
   ept_null_error EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_null_error, -1400);
   ept_no_loc_id EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_no_loc_id, -2291);
BEGIN
   INSERT INTO departments
   (department_id, department_name, manager_id, location_id)
   VALUES
   (v_dept_id, v_dept_name, v_mgr_id, v_loc_id);
   DBMS_OUTPUT.PUT_LINE('插入部门'||v_dept_id||'成功');
EXCEPTION
   WHEN DUP_VAL_ON_INDEX THEN
      RAISE_APPLICATION_ERROR(-20000, '部门编码不能重复');
   WHEN ept_null_error THEN
      RAISE_APPLICATION_ERROR(-20001, '部门编码、部门名称不能为空');
   WHEN ept_no_loc_id THEN
      RAISE_APPLICATION_ERROR(-20002, '没有该地点');
END insert_dept;

/*调用实例一:
DECLARE
   ept_20000 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20000, -20000);
   ept_20001 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20001, -20001);
   ept_20002 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20002, -20002);
BEGIN
   insert_dept(300, '部门300', 100, 2400);
   insert_dept(310, NULL, 100, 2400);
   insert_dept(310, '部门310', 100, 900);
EXCEPTION
   WHEN ept_20000 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20000部门编码不能重复');
   WHEN ept_20001 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20001部门编码、部门名称不能为空');
   WHEN ept_20002 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20002没有该地点');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('others出现了其他异常错误');
END;

调用实例二:
DECLARE
   ept_20000 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20000, -20000);
   ept_20001 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20001, -20001);
   ept_20002 EXCEPTION;
   PRAGMA EXCEPTION_INIT(ept_20002, -20002);
BEGIN
   insert_dept(v_dept_name => '部门310', v_dept_id => 310, 
               v_mgr_id => 100, v_loc_id => 2400);
   insert_dept(320, '部门320', v_mgr_id => 100, v_loc_id => 900);
EXCEPTION
   WHEN ept_20000 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20000部门编码不能重复');
   WHEN ept_20001 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20001部门编码、部门名称不能为空');
   WHEN ept_20002 THEN
      DBMS_OUTPUT.PUT_LINE('ept_20002没有该地点');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE('others出现了其他异常错误');
END;
*/

 
6.3.2 调用存储过程
 
    存储过程建立完成后,只要通过授权,用户就可以在SQLPLUS 、ORACLE开发工具或第三方开发工具中来调用运行。对于参数的传递也有三种:按位置传递、按名称传递和组合传递,传递方法与函数的一样。ORACLE 使用EXECUTE 语句来实现对存储过程的调用:
 
EXEC[UTE] procedure_name( parameter1, parameter2…);
 
例10:
 
EXECUTE logexecution;
 
例11:查询指定员工记录;
 

CREATE OR REPLACE
PROCEDURE QueryEmp
(v_empno IN  employees.employee_id%TYPE,
 v_ename OUT employees.first_name%TYPE,
 v_sal   OUT employees.salary%TYPE) 
AS
BEGIN
       SELECT last_name || last_name, salary INTO v_ename, v_sal 
    FROM employees 
    WHERE employee_id = v_empno; 
       DBMS_OUTPUT.PUT_LINE('温馨提示:编码为'||v_empno||'的员工已经查到!');
EXCEPTION
       WHEN NO_DATA_FOUND THEN 
      DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!');
      WHEN OTHERS THEN 
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END QueryEmp;
--调用
 DECLARE
    v1 employees.first_name%TYPE;
    v2 employees.salary%TYPE;
 BEGIN
   QueryEmp(100, v1, v2);
   DBMS_OUTPUT.PUT_LINE('姓名:'||v1);
   DBMS_OUTPUT.PUT_LINE('工资:'||v2);
   QueryEmp(103, v1, v2);
   DBMS_OUTPUT.PUT_LINE('姓名:'||v1);
   DBMS_OUTPUT.PUT_LINE('工资:'||v2);
   QueryEmp(104, v1, v2);
   DBMS_OUTPUT.PUT_LINE('姓名:'||v1);
   DBMS_OUTPUT.PUT_LINE('工资:'||v2);
END;

 
例12.计算指定部门的工资总和,并统计其中的职工数量。
 

CREATE OR REPLACE
PROCEDURE proc_demo
(
  dept_no NUMBER DEFAULT 10,
    sal_sum OUT NUMBER,
    emp_count OUT NUMBER
  )
IS
BEGIN
    SELECT SUM(salary), COUNT(*) INTO sal_sum, emp_count
  FROM employees WHERE department_id = dept_no;
EXCEPTION
   WHEN NO_DATA_FOUND THEN
      DBMS_OUTPUT.PUT_LINE('温馨提示:你需要的数据不存在!');
   WHEN OTHERS THEN
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END proc_demo;

DECLARE
V_num NUMBER;
V_sum NUMBER(8, 2);
BEGIN
  Proc_demo(30, v_sum, v_num);
DBMS_OUTPUT.PUT_LINE('温馨提示:30号部门工资总和:'||v_sum||',人数:'||v_num);
  Proc_demo(sal_sum => v_sum, emp_count => v_num);
DBMS_OUTPUT.PUT_LINE('温馨提示:10号部门工资总和:'||v_sum||',人数:'||v_num);
END;

       在PL/SQL 程序中还可以在块内建立本地函数和过程,这些函数和过程不存储在数据库中,但可以在创建它们的PL/SQL 程序中被重复调用。本地函数和过程在PL/SQL 块的声明部分定义,它们的语法格式与存储函数和过程相同,但不能使用CREATE OR REPLACE 关键字。
 
例13:建立本地过程,用于计算指定部门的工资总和,并统计其中的职工数量;
 

DECLARE
V_num NUMBER;
V_sum NUMBER(8, 2);
PROCEDURE proc_demo
  (
    Dept_no NUMBER DEFAULT 10,
    Sal_sum OUT NUMBER,
    Emp_count OUT NUMBER
  )
IS
BEGIN
    SELECT SUM(salary), COUNT(*) INTO sal_sum, emp_count 
    FROM employees WHERE department_id=dept_no;
EXCEPTION
   WHEN NO_DATA_FOUND THEN 
      DBMS_OUTPUT.PUT_LINE('你需要的数据不存在!');
   WHEN OTHERS THEN 
      DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
END proc_demo;
--调用方法:
BEGIN
    Proc_demo(30, v_sum, v_num);
DBMS_OUTPUT.PUT_LINE('30号部门工资总和:'||v_sum||',人数:'||v_num);
    Proc_demo(sal_sum => v_sum, emp_count => v_num);
DBMS_OUTPUT.PUT_LINE('10号部门工资总和:'||v_sum||',人数:'||v_num);
END;

6.3.3 AUTHID
过程中的AUTHID 指令可以告诉ORACLE ,这个过程使用谁的权限运行.默任情况下,存储过程会作为调用者的过程运行,但是具有设计者的特权.这称为设计者权利运行.
 
例14:建立过程,使用AUTOID DEFINER;
 

Connect HR/qaz
DROP TABLE logtable;
CREATE table logtable (userid VARCHAR2(10), logdate date);

CREATE OR REPLACE PROCEDURE logexecution 
    AUTHID DEFINER
IS
BEGIN
   INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
END;

GRANT EXECUTE ON logexecution TO PUBLIC;

CONNECT / AS SYSDBA
GRANT CONNECT TO testuser1 IDENTIFIED BY userpwd1;

CONNECT testuser1/userpwd1
INSERT INTO HR.LOGTABLE VALUES (USER, SYSDATE);
EXECUTE HR.logexecution

CONNECT HR/qaz
SELECT * FROM HR.logtable;

 
例15:建立过程,使用AUTOID CURRENT_USER;
 

CONNECT HR/qaz

CREATE OR REPLACE PROCEDURE logexecution 
  AUTHID CURRENT_USER
IS
BEGIN
   INSERT INTO logtable (userid, logdate) VALUES (USER, SYSDATE);
END;

GRANT EXECUTE ON logexecution TO PUBLIC;

CONNECT testuser1/userpwd1
INSERT INTO HR.LOGTABLE VALUES (USER, SYSDATE);
EXECUTE HR.logexecution

 
6.3.4 PRAGMA AUTONOMOUS_TRANSACTION
 
ORACLE8i 可以支持事务处理中的事务处理的概念.这种子事务处理可以完成它自己的工作,独立于父事务处理进行提交或者回滚.通过使用这种方法,开发者就能够这样的过程,无论父事务处理是提交还是回滚,它都可以成功执行.
 
例16:建立过程,使用自动事务处理进行日志记录;
 

DROP TABLE logtable;

CREATE TABLE logtable(
  Username varchar2(20),
  Dassate_time date,
  Mege varchar2(60)
);

CREATE TABLE temp_table( N number );

CREATE OR REPLACE PROCEDURE log_message(p_message varchar2)
  AS
  PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
  INSERT INTO logtable VALUES ( user, sysdate, p_message );
  COMMIT;
END log_message;

BEGIN
  Log_message (‘About to insert into temp_table‘);
  INSERT INTO temp_table VALUES (1);
  Log_message (‘Rollback to insert into temp_table‘);
  ROLLBACK;
END;

SELECT * FROM logtable;
SELECT * FROM temp_table;

 
例17:建立过程,没有使用自动事务处理进行日志记录;
 

CREATE OR REPLACE PROCEDURE log_message(p_message varchar2)
  AS
BEGIN
  INSERT INTO logtable VALUES ( user, sysdate, p_message );
  COMMIT;
END log_message;

BEGIN
  Log_message ('About to insert into temp_table');
  INSERT INTO temp_table VALUES (1);
  Log_message ('Rollback to insert into temp_table');
  ROLLBACK;
END;

SELECT * FROM logtable;
SELECT * FROM temp_table;

 
6.3.5 开发存储过程步骤
    开发存储过程、函数、包及触发器的步骤如下:
 
6.3.5.1 使用文字编辑处理软件编辑存储过程源码
    使用文字编辑处理软件编辑存储过程源码,要用类似WORD 文字处理软件进行编辑时,要将源码存为文本格式。
 
6.3.5.2 在SQLPLUS或用调试工具将存储过程程序进行解释
    在SQLPLUS或用调试工具将存储过程程序进行解释;
    在SQL>下调试,可用START 或GET 等ORACLE命令来启动解释。如:
SQL>START c:\stat1.sql
    如果使用调式工具,可直接编辑和点击相应的按钮即可生成存储过程。
 
6.3.5.3 调试源码直到正确
    我们不能保证所写的存储过程达到一次就正确。所以这里的调式是每个程序员必须进行的工作之一。在SQLPLUS下来调式主要用的方法是:
l         使用 SHOW ERROR命令来提示源码的错误位置;
l         使用 user_errors 数据字典来查看各存储过程的错误位置。
 
6.3.5.4 授权执行权给相关的用户或角色
如果调式正确的存储过程没有进行授权,那就只有建立者本人才可以运行。所以作为应用系统的一部分的存储过程也必须进行授权才能达到要求。在SQL*PLUS下可以用GRANT命令来进行存储过程的运行授权。
 
GRANT语法:
 

GRANT system_privilege | role 
TO user | role | PUBLIC [WITH ADMIN OPTION]

GRANT object_privilege | ALL ON schema.object 
TO user | role | PUBLIC [WITH GRANT OPTION]

--例子:

CREATE OR REPLACE PUBLIC SYNONYM dbms_job FOR dbms_job

GRANT EXECUTE ON dbms_job TO PUBLIC WITH GRANT OPTION

 
6.3.5.5 与过程相关数据字典
 
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;
 
6.3.6 删除过程和函数
 
1.删除过程
可以使用DROP PROCEDURE命令对不需要的过程进行删除,语法如下:
DROP PROCEDURE [user.]Procudure_name;
 
2.删除函数
可以使用DROP FUNCTION 命令对不需要的函数进行删除,语法如下:
 

DROP FUNCTION [user.]Function_name;

--删除上面实例创建的存储过程与函数
DROP PROCEDURE logexecution;
DROP PROCEDURE delemp;
DROP PROCEDURE insertemp;
DROP PROCEDURE fireemp;
DROP PROCEDURE queryemp;
DROP PROCEDURE proc_demo;
DROP PROCEDURE log_message;
DROP FUNCTION demo_fun;
DROP FUNCTION get_salary;

  指存储在数据库中供所有用户程序调用的子程序叫存储过程、存储函数。
存储过程没有返回值。存储函数有返回值
    
  创建存储过程
      用CREATE PROCEDURE命令建立存储过程和存储函数。
 
      语法:
create [or replace] PROCEDURE过程名(参数列表) 
AS
        PLSQL子程序体;
    
  存储过程示例:为指定的职工在原工资的基础上长10%的工资
 
/*
为指定的职工在原工资的基础上长10%的工资,并打印工资前和工资后的工资
*/
SQL> create or replace procedure raiseSalary(empid in number)
    as
    pSal emp.sal%type;--保存员工当前 工资
    begin
--查询该员工的工资
    select sal into pSal from emp where empno=empid;
--给该员工涨工资
    update emp set sal = sal*1.1 where empno=empid;
--打印涨工资前后的工资
    dbms_output.put_line('员工号:' || empid || '涨工资前
   ' || psal || '涨工资后' || psal*1.1);
    end;
 1  /
 
Procedure created
--存储过程调用
--方法一
SQL> set serveroutput on
SQL> exec raisesalary(7369);
 
员工号:7369涨工资前
800涨工资后880
 
方法二
    set serveroutput on
begin
 raisesalary(7369);
end;
/
 
PL/SQL procedure successfully completed
 
 
       存储函数
      函数(Function)为一命名的存储程序,可带参数,并返回一计算值。函数和过程的结构类似,但必须有一个RETURN子句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型等。
 
     建立存储函数的语法:
 
CREATE [OR REPLACE] FUNCTION函数名(参数列表)
 RETURN 函数值类型
AS
PLSQL子程序体;
 
 
      示例:查询某职工的年收入。
SQL> /**/
    /*
    查询某职工的总收入
    */
    create or replace function queryEmpSalary(empid in number)
    return number
   as
    pSal number; --定义变量保存员工的工资
    pComm number; --定义变量保存员工的奖金
   begin
   select sal,comm into psal,pcomm from emp where empno = empid;
   return psal*12+nvl(pcomm,0);
   end;
   /
 
Function created
 
l        函数的调用
 
SQL> declare
    v_sal number;
    begin
    v_sal:=queryEmpSalary(7934);
    dbms_output.put_line('salary is:'|| v_sal);
    end;
    /
 
salary is:15600
 
PL/SQL procedure successfully completed
 
SQL> begin
    dbms_output.put_line('salary is:'|| queryEmpSalary(7934));
    end;
    /
 
salary is:15600
 
PL/SQL procedure successfully completed
 
 
       触发器
       数据库触发器是一个与表相关联的、存储的PL/SQL程序。每当一个特定的数据操作语句(Insert,update,delete)在指定的表上发出时,Oracle自动地执行触发器中定义的语句序列。
 
       触发器的类型
         语句级触发器
        在指定的操作语句操作之前或之后执行一次,不管这条语句影响了多少行。
 
         行级触发器(FOR EACH ROW)
        触发语句作用的每一条记录都被触发。在行级触发器中使用old和new伪记录变量,识别值的状态。
 
      创建触发器
CREATE  [or REPLACE] TRIGGER 触发器名
   {BEFORE | AFTER}
   {DELETE | INSERT | UPDATE [OF列名]}
   ON  表名
   [FOR EACH ROW [WHEN(条件) ] ]
   PLSQL 块
 
       示例1:限制非工作时间向数据库插入数据
SQL> create or replace
    trigger securityEmp
    before insert on emp
    declare
    begin
    if to_char(sysdate,'day')in('星期四','星期六','星期日')
    or to_number(to_char(sysdate,'hh24'))not between 8 and 18 then
    raise_application_error(-20001,'不能在非工作时间插入数据。');
    end if;
   end;
   /
 
Trigger created
 
      触发语句与伪记录变量的值
触发语句
:old
:new
Insert
所有字段都是空(null)
将要插入的数据
Update
更新以前该行的值
更新后的值
delete
删除以前该行的值
所有字段都是空(null)
 
      示例2:确认数据(检查emp表中sal的修改值不低于原值)
SQL> create or replace trigger checkSal
    before update of sal on emp
    for each row
    declare
    begin
    if :new.sal<:old.sal then
    raise_application_error(-20001,'更新后的薪水比更新前小');
    end if;
    end;
   /
 
Trigger created
运行后结果:
SQL> update emp set sal=260 where empno=7499;
 
update emp set sal=260 where empno=7499
 
ORA-20001: 更新后的薪水比更新前小
ORA-06512: 在 "SCOTT.CHECKSAL", line 4
ORA-04088: 触发器 'SCOTT.CHECKSAL'执行过程中出错
 
       触发器总结
      触发器可用于
•         数据确认 
•         实施复杂的安全性检查
•         做审计,跟踪表上所做的数据操作等
 
      查询触发器、过程及函数
•         Select * from user_triggers;
•         Select * from user_source;


 一.存储过程(PROCEDURE)

  使用过程, 不仅可以简化客户端应用程序的开发和维护,而且可以提高应用程序的运行性CREATE [OR REPLACE] PROCUDURE procedure_name   (
arg1 [model1] datatype1,
arg2[model2] datatype2)   IS [AS]   PL/SQL Block;   

arg1,arg2用于指定过程的参数,IS/AS用于开始一个PL/SQL块.当指定参数数据类型时,不能指定其长度.   

在建立过程的时间,既可以指定输入参数(IN),也可以指定输出参数(OUT)以及输入输出参数(IN OUT).如果不定义参数模式,则默认为输入参数,如果要定义输出参数,则需要指定OUT关键字,如果定义输入输出参数,   要指定IN OUT关键字. 一般使用execute(或exec) 过程名 或者 call 过程名来调用过程.

  set serveroutput on打开oracle 的输出.   (1)不带参数的过程   CREATE OR REPLACE PROCEDURE out_time   IS   BEGIN   dbms_output.put_line(systimestamp);   END;   call out_time();或者 exec out_time;   (2)带IN参数的过程   如果不指定参数模式,则默认的为IN,也可以显示的指定输入模式IN.   CREATE OR REPLACE PROCEDURE add_employee   (eno NUMBER,name VARCHAR2,sal NUMBER,job VARCHAR2 DEFAULT 'CCC', dno NUMBER)   IS   BEGIN   INSERT INTO emp(empno, ename,sal,job,deptno)   VALUES(eno,ename,sal,job,dno);   EXCEPTION   WHEN DUP_VAL_ON_INDEX THEN   RAISE_APPLICATION_ERROR(-20000, '雇员不能重复');   END;   除了默认值的参数外,其余的都要输入.   (3)带OUT的过程   在过程中输出结果是使用OUT或者IN OUT来完成的.   定义输出参数的时间,必须要使用OUT来定义输出.   CREATE OR REPLACE PROCEDURE query_employee   (eno NUMBER,name OUT VARCHAR2,sal OUT NUMBER)   IS   BEGIN   SELECT ename, sal INTO name, sal FROM emp WHERE empno=eno;   EXCEPTION   WHEN NO_DATA_FOUND THEN   RAISE_APPLICATION_ERROR(-20000, '雇员不存在');   END;   对于指定为OUT类型的参数,必须定义变量接收输出参数的数据.调用实例:   SQL> var name VARCHAR2(10)   SQL> var sal NUMBER   SQL> exec query_employee(77,:name,:sal)   SQL> print name sal   (4)带IN和OUT的过程   在调用前要通过变量给参数传递数据,在调用结束后,会通过此变量将值传递给应用程序   CREATE OR PROCEDURE compute   (num IN OUT NUMBER, num2 IN OUT NUMBER)   IS   v1 NUMBER;   v2 NUMBER;   BEGIN   v1:=num1/num2;   v2:=MOD(num1, num2);   num1 := v1;   num2 := v2;   END;

  在应用程序调用IN OUT的存储过程时,必须提供两个变量临时存放数值,运行如下:   SQL> var n1 NUMBER   SQL> var n2 NUMBER   SQL> exec :n1:=100   SQL> exec :n2:=30   SQL> exec compute(:n1, :n2)   SQL> PRINT n1 n2   (5)为参数传递变量和数据

  [1]位置传递   如 SQL> exec add_dept(50, 'SALES', 'NEW YORK')   [2]名称传递,使用=>符号来指定值   如 SQL> exec add_dept(dno=>50)   [3]组合传递,以上两种传递方法交替   (6)查看过程的源代码   过程名,源代码,执行代码放到了数据字典中,通过查询数据字典USER_SOURCE,可以显示当前用户定义的过程   SELECT text FROM user_source WHERE name='ADD_DEPT';   (7)删除过程   DROP PROCEDURE 过程名

二.函数 (FUNCTION)

  CREATE [OR REPLACE] FUNCTION function_name   (arg1 [model1] datatype1,arg2 [model2] datatype2)   RETURN datatype   IS|AS   PL/SQL Block;   arg1,arg2指定函数 的参数,当不指定参数数据类型时,不能指定其长度   RETURN 指定函数 返回的数据类型.注意,函数 前面必须要有RETURN子句.在函数 体内至少含有一条RETURN子句.函数 的参数MODEl和过程的一样   (1)不带参数的函数   CREATE OR REPLACE FUNCTION get_user   RETURN VARCHAR2   IS   v_user VARCHAR2(100);   BEGIN   SELECT username INTO v_user FROM user_users;   RETURN v_user;   END;   调用方法   SQL> var v1 VARCHAR2(100)   SQL> exec :v1:=get_user   SQL> PRINT v1   (2)带IN参数   CREATE OR REPLACE FUNCTION get_sal(name IN VARCHAR2)   RETURN NUMBER   IS   v_sal emp.sal%TYPE;   BEGIN   SELECT sal INTO v_sal FROM emp   WHERE upper(ename)=upper(name);   RETURN v_sal;   EXCEPTION   WHEN NO_DATA_FOUND THEN   raise-application_error(-20000,'该雇员不存在');   END;   调用方法如下:   SQL> var sal NUMBER   SQL> exec :sal:=get_sal('scott')   SQL> print sal   (3)带OUT参数   一般情况下,函数 只需要单个返回数据,如果希望使用函数 同事返回多个数据,就需要用到输出参数了.   CREATE OR REPLACR FUNCTION get_info   (name VARCHAR2, title OUT VARCHAR2)   RETURN VARCHAR2   AS   deptname dept.dname%TYPE;   BEGIN   SELECT a.job,b.dname INTO title, deptname   FROM emp a, dept b   WHERE a.deptno = b.deptno   AND upper(a.ename) = upper(a.ename)   RETURN deptname;   EXCEPTION   WHEN NO_DATA_FOUND THEN   raise_application_erro(-20000, '雇员不存在');   END;   由于此函数 带有OUT参数,所以要定义变量接收OUT参数和函数 的返回值   SQL> var job varchar2(20)   SQL> var dname varchar2920)   SQL> exec :dname:=getinfo('scott',:job)   SQL> print dname job   (4)带IN OUT参数   在调用函数 之前需要通过变量给该种参数传递数据   CREATE OR REPLACE FUNCTION result   (num1 NUMBER, num2 IN OUT NUMBER)   RETURN NUMBER   AS   v_result NUMBER(6);   v_remain NUMBER;   BEGIN   v_result:=num1/num2;   v_remain:=MOD(num1, num2);   num2 := v_remain;   RETURN v_result;   EXCEPTION   WHEN ZERO_DIVIDE THEN   raise_application_error(-20000, '不能除0');   END;   执行如下:   SQL> var result1 NUMBER   SQL> var result2 NUMBER   SQL> exec :result2:=30   SQL> exec :result1:=result(100, :result2)   SQL> print result1 result2   (5)函数 的删除   DROP FUNCTION 函数 名

三.子程序的管理   列出当前用户的子程序   数据字典视图USER_OBJECTS显示当前用户所包含的所有对象.可以列出用户的表,视图,索引,也可以列出用户的过程,函数 等.   SELECT object_name, created, status FROM user_object2 WHERE object_type IN ('PROCEDURE', 'FUNCTION');
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值