Oracle --- 存储过程函数包游标触发器

Posted 劳埃德·福杰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Oracle --- 存储过程函数包游标触发器相关的知识,希望对你有一定的参考价值。

存储过程和函数也是一种PL/SQL块,是存入数据库的PL/SQL块。
但存储过程和函数不同于已经介绍过的PL/SQL程序,我们通常把PL/SQL程序称为无名块,而存储过程和函数是以命名的方式存储于数据库中的。

1.存储过程

创建存储过程,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系统权限

AS关键字后面可以定义过程的局部变量

CREATE [OR REPLACE] PROCEDURE 存储过程名[(参数[IN|OUT|IN OUT] 数据类型...)]
AS|IS[说明部分]
BEGIN         
    可执行部分
[EXCEPTION 错误处理部分]
END [过程名];

创建一个显示雇员总人数的存储过程

CREATE OR REPLACE PROCEDURE EMP_COUNT
AS
V_TOTAL NUMBER(10);
BEGIN
    SELECT COUNT(*) INTO V_TOTAL FROM EMP;
    DBMS_OUTPUT.PUT_LINE('雇员总人数为:'||V_TOTAL);
END;

参数传递

编写给雇员增加工资的存储过程CHANGE_SALARY,通过IN类型的参数传递要增加工资的雇员编号和增加的工资额。 

CREATE OR REPLACE PROCEDURE CHANGE_SALARY(P_EMPNO IN NUMBER DEFAULT 7788,P_RAISE NUMBER DEFAULT 10)
AS
    V_ENAME VARCHAR2(10);
    V_SAL NUMBER(5);
BEGIN
    SELECT ENAME,SAL INTO V_ENAME,V_SAL FROM EMP WHERE EMPNO=P_EMPNO;
	UPDATE EMP SET SAL=SAL+P_RAISE WHERE EMPNO=P_EMPNO;
	DBMS_OUTPUT.PUT_LINE('雇员'||V_ENAME||'的工资被改为'||TO_CHAR(V_SAL+P_RAISE));
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
 	DBMS_OUTPUT.PUT_LINE('发生错误,修改失败!');
 	ROLLBACK;
END;

使用OUT类型的参数返回存储过程的结果

CREATE OR REPLACE PROCEDURE EMP_COUNT(P_TOTAL OUT NUMBER)
AS
BEGIN
    SELECT COUNT(*) INTO P_TOTAL FROM EMP;
END;

使用IN OUT类型的参数,给电话号码增加区码。 

CREATE OR REPLACE PROCEDURE ADD_REGION(P_HPONE_NUM IN OUT VARCHAR2)
AS
BEGIN
    P_HPONE_NUM:='024-'||P_HPONE_NUM;
END;

删除存储过程

DROP PROCEDURE 存储过程名;

2.函数

创建函数,需要有CREATE PROCEDURE或CREATE ANY PROCEDURE的系统权限

CREATE [OR REPLACE] FUNCTION 函数名[(参数[IN] 数据类型...)] RETURN 数据类型
AS|IS
	[说明部分]
BEGIN
    可执行部分
    RETURN (表达式)
[EXCEPTION
    错误处理部分]
END [函数名];

删除函数

DROP FUNCTION 函数名;

创建一个通过雇员编号返回雇员名称的函数GET_EMP_NAME。

CREATE OR REPLACE FUNCTION GET_EMP_NAME(P_EMPNO NUMBER DEFAULT 7788) RETURN VARCHAR2
AS
V_ENAME VARCHAR2(10);
BEGIN
    SELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO=P_EMPNO;
    RETURN(V_ENAME);
EXCEPTION
    WHEN NO_DATA_FOUND THEN
        DBMS_OUTPUT.PUT_LINE('没有该编号雇员!');
        RETURN (NULL);
    WHEN TOO_MANY_ROWS THEN
        DBMS_OUTPUT.PUT_LINE('有重复雇员编号!');
        RETURN (NULL);
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('发生其他错误!');
        RETURN (NULL);
END;

3.包

包是用来存储相关程序结构的对象,它存储于数据字典中。
包由两个分离的部分组成:包头(PACKAGE)和包体(PACKAGE BODY)
包头是包的说明部分,是对外的操作接口,对应用是可见的;包体是包的代码和实现部分,对应用来说是不可见的黑盒。

出现在包头中的称为公有元素
出现在包体中的称为私有元素
出现在包体的过程(或函数)中的称为局部变量

系统包:DBMS_OUTPUT包...

创建管理雇员信息的包EMPLOYE,它具有从EMP表获得雇员信息,修改雇员名称和写回EMP表的功能。

CREATE OR REPLACE PACKAGE EMPLOYE --包头部分 
IS
    PROCEDURE SHOW_DETAIL; 
    PROCEDURE GET_EMPLOYE(P_EMPNO NUMBER); 
    PROCEDURE SAVE_EMPLOYE; 
END EMPLOYE;

CREATE OR REPLACE PACKAGE BODY EMPLOYE --包体部分 
IS
EMPLOYE EMP%ROWTYPE;
-------------- 显示雇员信息 ---------------
PROCEDURE SHOW_DETAIL
AS
BEGIN
    DBMS_OUTPUT.PUT_LINE(‘----- 雇员信息 -----’);
    DBMS_OUTPUT.PUT_LINE('雇员编号:'||EMPLOYE.EMPNO);
    DBMS_OUTPUT.PUT_LINE('雇员名称:'||EMPLOYE.ENAME);
    DBMS_OUTPUT.PUT_LINE('雇员职务:'||EMPLOYE.JOB);
    DBMS_OUTPUT.PUT_LINE('雇员工资:'||EMPLOYE.SAL);
    DBMS_OUTPUT.PUT_LINE('部门编号:'||EMPLOYE.DEPTNO);
END SHOW_DETAIL;
---------------------- 保存雇员到EMP表 --------------------------
PROCEDURE SAVE_EMPLOYE
AS
BEGIN
    UPDATE EMP 
    SET ENAME=EMPLOYE.ENAME,SAL=EMPLOYE.SAL
    WHERE EMPNO=EMPLOYE.EMPNO;
 	DBMS_OUTPUT.PUT_LINE('雇员信息保存完成!');
END SAVE_EMPLOYE;
---------------------------- 修改雇员名称 ------------------------------
PROCEDURE CHANGE_NAME(P_NEWNAME VARCHAR2)
AS
BEGIN
    EMPLOYE.ENAME:=P_NEWNAME;
    DBMS_OUTPUT.PUT_LINE('修改名称完成!');
END CHANGE_NAME;
// 测试
SET SERVEROUTPUT ON
EXECUTE EMPLOYE.GET_EMPLOYE(7788);
EXECUTE EMPLOYE.SHOW_DETAIL;
EXECUTE EMPLOYE.SAVE_EMPLOYE;

4.游标

游标(CURSOR)是Oracle系统在内存中开辟的一个工作区,在其中存放SELECT语句返回的查询结果。游标工作区中,存在着一个指针(POINTER),在初始状态它指向查询结果的首记录。

游标的类型

  • 显式游标:由用户定义、操作,用于处理返回多行数据的SELECT查询。
  • 隐式游标(又称SQL游标):由系统自动进行操作,用于处理DML语句和返回单行数据的SELECT查询。 (所有的SQL语句都有一个执行的缓冲区,隐式游标就是指向该缓冲区的指针,由系统隐含地打开、处理和关闭。)

声明游标:就是使一个游标与一条查询语句建立联系。
CURSOR <游标名>[(参数1 数据类型[,…n])] IS 查询语句;
打开游标:就是执行游标定义时所对应的查询语句,并把查询返回的结果集存储在游标对应的工作区中。     
OPEN <游标名>[(参数1 [,…n])];
提取游标数据:就是从定义游标的工作区中检索一条数据记录作为当前数据记录。
FETCH <游标名> INTO 变量1 [,… n]
关闭游标:CLOSE <游标名>; 

游标属性 

  • %ISOPEN 描述游标是否已经打开,返回布尔型值。
  • %FOUND 描述最近一次FETCH操作的执行情况,返回布尔型值。如果最近一次使用FETCH语句提取游标数据得到结果则返回TRUE,否则返回FALSE。
  • %NOTFOUND 用于描述最近一次FETCH操作的执行情况,返回布尔型值。但与%FOUND属性不同的是,如果最近一次使用FETCH语句提取游标数据没有得到结果则返回TRUE,否则返回FALSE。
  • %ROWCOUNT 用于描述截至目前从游标工作区提取的实际记录数。 
DECLARE
    v_deptno emp.deptno%TYPE;
    CURSOR c_emp IS SELECT * FROM emp WHERE deptno=v_deptno;
    v_emp c_emp%ROWTYPE;
BEGIN
    v_deptno:=&x;
    OPEN c_emp;
	LOOP
    	 FETCH c_emp INTO v_emp;
         EXIT WHEN c_emp%NOTFOUND;
         DBMS_OUTPUT.PUT_LINE(v_emp.empno||' '||v_emp.ename||' '||    v_emp.sal ||'  '||  v_deptno);                                   
    END LOOP; 
    CLOSE c_emp;
END; 

5.触发器

触发器是一种在事件发生时隐式地自动执行的PL/SQL块,不能接受参数,不能被显式调用。
分为两类:

  • 系统触发器:由系统事件触发的PL/SQL,比如登陆oracle数据库,登出oracle。
  • DML触发器:由DML语句触发的PL/SQL,比如INSERT、UPDATE、DELETE等。

我们一般用到的是DML触发器。

语句触发器:每次对表进行DML操作时只触发一次

CREATE [OR REPLACE] TRIGGER trigger_tname
   [before|after]    --在触发事件前还是后发生
   [update | OR insert | OR delete]    --触发事件
   ON table_name           --是哪张表
declare
 --变量声明区
begin
  trigger_body        --需要触发的事件
end;

定义一个不在规定时间无法添加往students表中添加数据的触发器

create or replace trigger dml_students_time
   before --在触发事件发生前
   insert or update or delete --触发事件为增删改
   on students --对表students
begin --执行触发器体
   if to_char(sysdate,'HH24:MI') not between '08:00' and '18:00' 
      or to_char(sysdate,'DY') in ('SAT','SUN') 
   then 
      dbms_output.put_line('You may only make changes during normal office hours');
   end if;
end ;
/

行级触发器:数据库表中的每一行受影响就触发一次触发器

// 创建和测试行触发器
CREATE [OR REPLACE] TRIGGER trigger_tname
     [before|after]    --在触发事件前还是后发生
     [update of 字段 | OR insert of 字段 | OR delete of 字段] --触发事件of后可选择,不写代表整个表的所有行
      ON table_name --是哪张表
     [REFERENCING OLD AS old|NEW AS new]  
      FOR EACH ROW
     [WHEN(condition)]
declare
 --变量声明区
begin
  trigger_body --需要触发的事件
end;
/

 创建触发器,用来记录表的删除数据

--创建表
CREATE TABLE employee(
  id  VARCHAR2(4) NOT NULL,
  name VARCHAR2(15) NOT NULL,
  age NUMBER(2)  NOT NULL,
  sex CHAR NOT NULL
);
--插入数据
INSERT INTO employee VALUES('e101','zhao',23,'M');
INSERT INTO employee VALUES('e102','jian',21,'F');
--创建记录表(包含数据记录)
CREATE TABLE old_employee AS SELECT * FROM employee;

--创建触发器
CREATE OR REPLACE TRIGGER TIG_OLD_EMP
 AFTER DELETE ON EMPLOYEE
 FOR EACH ROW --语句级触发,即每一行触发一次
BEGIN
 INSERT INTO OLD_EMPLOYEE VALUES (:OLD.ID, :OLD.NAME, :OLD.AGE, :OLD.SEX); --:old代表旧值
END;
/

--下面进行测试
DELETE employee;
SELECT * FROM old_employee;

以上是关于Oracle --- 存储过程函数包游标触发器的主要内容,如果未能解决你的问题,请参考以下文章

Oracle --- 存储过程函数包游标触发器

oracle--存储过程,游标,函数,触发器

Oracle03——游标异常存储过程存储函数触发器和Java代码访问Oracle对象

oracle存储过程的使用

oracle-游标-存储过程-函数-包

Oracle-4 - :超级适合初学者的入门级笔记:plsql,基本语法,记录类型,循环,游标,异常处理,存储过程,存储函数,触发器