一、存储过程、存储函数
1、What's This?
①、ORACLE 提供可以把 PL/SQL 程序存储在数据库中,并可以在任何地方来运行它。这样就叫存储过程或函数。
②、存储过程、存储函数的唯一区别是存储函数有返回值,而存储过程没有返回值。
2、创建存储函数
eg、根据部门号返回部门总工资
CREATE OR REPLACE FUNCTION get_salary( dep_id IN employees.department_id%TYPE DEFAULT 10, -- 输入参数, 可以设置默认值 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 = dep_id; RETURN v_sum; -- 返回值 EXCEPTION WHEN no_data_found THEN dbms_output.put_line('No data found'); WHEN OTHERS THEN dbms_output.put_line('Other Exception'); END;
执行、删除存储函数
-- 执行该函数 DECLARE v_sum NUMBER; v_count NUMBER; BEGIN v_sum := get_salary(80, v_count); -- v_sum := get_salary(dep_id => 80, emp_count => v_count); dbms_output.put_line('80号部门工资总数: ' || v_sum || ',人数:' || v_count); END; -- 删除 DROP FUNCTION get_salary;
3、存储过程
eg、根据员号查询员工工资
CREATE OR REPLACE PROCEDURE query_emp_salary( v_empid employees.employee_id%TYPE, v_name OUT employees.last_name%TYPE, v_sal OUT employees.salary%TYPE ) AS BEGIN SELECT last_name, salary INTO v_name, v_sal FROM employees WHERE employee_id = v_empid; EXCEPTION WHEN no_data_found THEN dbms_output.put_line('No data found'); WHEN OTHERS THEN dbms_output.put_line('Other Exception'); END;
执行、删除存储过程
-- 执行该存储过程 DECLARE v_1 employees.employee_id%TYPE; v_2 employees.last_name%TYPE; BEGIN query_emp_salary(60, v_1, v_2); dbms_output.put_line('name: : ' || v_1 || ',salary:' || v_2); END; -- 删除 DROP PROCEDURE query_emp;
二、包的创建和应用
1、What's 包?
①、包是一组相关过程、函数、变量、常量和游标等 PL/SQL 程序设计元素的组合,是对这些 PL/SQL 程序设计元素的封装。(好像JAVA中的类,可以定义各种方法)
②、使用包不仅可以使程序设计模块化,还可以对外隐藏包内所使用的信息(通过使用私
用变量)
③、当程序首次调用包内函数或过程时, ORACLE 将整个包调入内存,当再次访问包内元素时, ORACLE 直接从内存中读取,而不需要进行磁盘 I/O 操作,从而使程序执行效率得到提高。
2、包的组成(包定义、包主体)
①、包定义(PACKAGE):声明包内数据类型、游标、函数、过程、异常,这些元素为包的公有元素。
②、包主体(PACKAGE BODY):包定义部分的具体实现,在包主体中可以定义包的私有元素
注意:包定义和包主体分开编译,并作为两部分分开的对象存放在数据库字典中,详见数据字典 user_source、all_source、 dba_source.
3、包的实现:
eg、创建的包为 demo_pack, 该包中包含一个记录变量 DeptRec、一个函数和一个过程。
包定义:
-- demo_pack 包定义: CREATE OR REPLACE PACKAGE demo_pack AS deptRec departments%ROWTYPE; -- 声明游标 FUNCTION add_dept(dept_no NUMBER, dept_name VARCHAR2) -- 声明函数 RETURN NUMBER; PROCEDURE query_dept(dept_no IN NUMBER); -- 声明存储过程 END demo_pack;
包主体:
-- demo_pack 包主体: CREATE OR REPLACE PACKAGE BODY demo_pack AS -- 创建 add_demp 函数 FUNCTION add_dept(dept_no NUMBER, dept_name VARCHAR2) RETURN NUMBER AS empno_remaining EXCEPTION; -- 声明异常 PRAGMA exception_init(empno_remaining, -1); -- -1位违反唯一约束错误 BEGIN INSERT INTO departments(department_id, department_name) VALUES(dept_no, dept_name); IF SQL%FOUND THEN RETURN 1; END IF; EXCEPTION WHEN empno_remaining THEN RETURN 0; WHEN OTHERS THEN RETURN -1; END add_dept; -- 创建 query_dept 存储过程 PROCEDURE query_dept(dept_no IN NUMBER) AS BEGIN SELECT * INTO deptRec FROM departments WHERE department_id = dept_no; EXCEPTION WHEN no_data_found THEN dbms_output.put_line('no_data_found exception'); WHEN OTHERS THEN dbms_output.put_line('Other exception'); END query_dept; BEGIN NULL; END demo_pack;
demo_pack 包的调用:对包内共有元素的调用格式为:包名.元素名称
DECLARE v1 NUMBER; BEGIN v1 := demo_pack.add_dept(900, 'dept-A'); IF v1 = -1 THEN dbms_output.put_line(SQLCODE || ': ' || SQLERRM); ELSIF v1 = 0 THEN dbms_output.put_line('部门存在啊'); ELSE dbms_output.put_line('SUCCESS'); demo_pack.query_dept(900); dbms_output.put_line('ID: ' || demo_pack.deptRec.department_id || ', dept-name: ' || demo_pack.deptRec.department_name ); END IF; END;
三、触发器
1、What's 触发器?
①、触发器是当某个事件发生时自动地触发另一个事件。所以运行触发器就叫触发或点火(^^)。
②、ORACLE 事件指的是对数据库的表进行的INSERT、 UPDATE 及 DELETE 操作或对视图进行类似的操作。 ORACLE 将触发器的功能扩展到了触发 ORACLE,如数据库的启动与关闭等。
2、触发器类型
2.1 DML 触发器
可以在 DML 操作前或操作后进行触发,并且可以对每个行或语句操作上进行触发。
2.2 替代触发器
由于在 ORACLE 里,不能直接对由两个以上的表建立的视图进行操作。所以给出了替代触发器。
2.3 系统触发器
它可以在 ORACLE 数据库系统的事件中进行触发,如 ORACLE 系统的启动与关闭等
3、触发器组成:
触发事件:INSERT, UPDATE, DELETE。
触发时间:触发事件发生之前( BEFORE)还是之后(AFTER)触发
触发器本身: TRIGGER 触发之后的目的和意图
触发频率:说明触发器内定义的动作被执行的次数。即语句级(STATEMENT)触发器和行级(ROW)触发器。
注意:
①、语句级(STATEMENT)触发器:是指当某触发事件发生时,该触发器只执行一次;
行级(ROW)触发器:是指当某触发事件发生时,对受到该操作影响的每一行数据,触发器都单独执行一次。
②、当省略 FOR EACH ROW 选项时, BEFORE 和 AFTER 触发器为语句触发器,而 INSTEAD OF 触发器则为行触发器。
4、创建触发器
4.1、DML 触发器
类型:
BEFORE INSERT BEFORE INSERT FOR EACH ROW AFTER INSERT AFTER INSERT FOR EACH ROW BEFORE UPDATE BEFORE UPDATE FOR EACH ROW AFTER UPDATE AFTER UPDATE FOR EACH ROW BEFORE DELETE BEFORE DELETE FOR EACH ROW AFTER DELETE AFTER DELETE FOR EACH ROW
问题:当触发器被触发时,要使用被插入、更新或删除的记录中的列值,有时要使用操作前、后列的值。
解决: :new ------修饰符访问操作完成后列的值
:old ------修饰符访问操作完成前列的值
eg、把职工表被删除记录写到职工表删除日志表中:
CREATE OR REPLACE TRIGGER del_emp_trigger BEFORE DELETE ON employees FOR EACH ROW BEGIN INSERT INTO emp_temp(employee_id, first_name, last_name) VALUES(:old.employee_id, :old.first_name, :OLD.last_name); END;
4.2、替代(INSTEAD OF)触发器
①、INSTEAD OF 触发器,只能对视图和对象视图建立 INSTEAD OF触发器,而不能对表、模式和数据库建立 INSTEAD OF 触发器
②、只能对视图和对象视图建立 INSTEAD OF触发器,而不能对表、模式和数据库建立 INSTEAD OF 触发器
eg、删除视图emp_view 中的数据
-- 删除视图数据非法 DELETE FROM emp_view WHERE employee_id = 100 -- 解决方法: INSTEAD OF 触发器 CREATE OR REPLACE TRIGGER emp_view_del INSTEAD OF DELETE ON emp_view FOR EACH ROW BEGIN DELETE FROM employees WHERE employee_id = :old.employee_id; END emp_view_del;
4.3、创建系统事件触发器
系统事件触发器可以在 DDL 或数据库系统上被触发。
①、DDL 指的是数据定义语言,如CREATE 、 ALTER 及 DROP 等。
②、数据库系统事件包括数据库服务器的启动或关闭,用户的登录与退出、数据库服务错误等。
4.4、触发器状态
注意: 当删除表或视图时,建立在这些对象上的触发器也随之删除。
有效状态(ENABLE):当触发事件发生时,处于有效状态的数据库触发器 TRIGGER 将被触发。
无效状态(DISABLE):当触发事件发生时,处于无效状态的数据库触发器 TRIGGER 将不会被触发,就跟没有这个数据库触发器(TRIGGER) 一样
FOR EXAMPLE:
ALTER TRIGGER emp_view_delete DISABLE(ENABLE);