肥宅兜

导航

触发器

 

 

 

 

 

范例: 只有在每个月的10日才允许办理新雇员入职与离职,其他时间不允许增加新雇员数据

CREATE OR REPLACE TRIGGER forbid_emp_trigger

BEFORE INSERT OR DELETE

ON emp

DECLARE

v_currentdate VARCHAR(20) ;

BEGIN

SELECT TO_CHAR(SYSDATE,'dd') INTO v_currentdate FROM dual ;

IF TRIM(v_currentdate)!='10' THEN

RAISE_APPLICATION_ERROR(-20008,'在每月的10号才允许办理入职手续!') ;

END IF ;

END ;

/

 

 

范例:emp表之中增加新雇员信息,当前不是当月10

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (8998,'MLDN','MANAGER',SYSDATE,2000,500,7369,40) ;

 

范例:在星期一、周末以及每天下班时间(每天9:00以前、18:00以后)后不允许更新emp数据表

CREATE OR REPLACE TRIGGER forbid_emp_trigger

BEFORE INSERT OR DELETE OR UPDATE

ON emp

DECLARE

v_currentweak VARCHAR(20) ;

v_currenthour VARCHAR(20) ;

BEGIN

SELECT TO_CHAR(SYSDATE,'day'),TO_CHAR(SYSDATE,'hh24') INTO v_currentweak, v_currenthour FROM dual ;

IF TRIM(v_currentweak)='星期一' OR TRIM(v_currentweak)='星期六' OR TRIM(v_currentweak)='星期日' THEN

RAISE_APPLICATION_ERROR(-20008,'在周末及周一不允许更新emp数据表!') ;

ELSIF TRIM(v_currenthour)<'9' OR TRIM(v_currenthour)>'18' THEN

RAISE_APPLICATION_ERROR(-20009,'在下班时间不能够修改emp表数据!') ;

END IF ;

END ;

/

 

范例:假设当前日期时间为周日,发出以下增加数据操作

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (8998,'MLDN','MANAGER',SYSDATE,2000,500,7369,40) ;

 

 

范例:在每天12点以后,不允许修改雇员工资和佣金

CREATE OR REPLACE TRIGGER forbid_emp_trigger

BEFORE UPDATE OF sal,comm

ON emp

DECLARE

v_currenthour VARCHAR(20) ;

BEGIN

SELECT TO_CHAR(SYSDATE,'hh24') INTO v_currenthour FROM dual ;

IF TRIM(v_currenthour)>'12' THEN

RAISE_APPLICATION_ERROR(-20009,'每天12点以后不允许更新雇员工资、佣金。') ;

END IF ;

END ;

/

 

范例:当前时间已超过中午12点,更新emp表中的salcomm字段

UPDATE emp SET sal=9000,comm=5000 WHERE empno=7369 ;

 

范例:每一位雇员都要根据其收入上缴所得税,假设所得税的上缴原则为:2000以下上缴3%2000 ~ 5000上缴8%5000以上上缴10%,现在要求建立一张新的数据表,可以记录出雇员的编号、姓名、工资、佣金、上缴所得税数据,并且在每次修改雇员表中salcomm字段后可以自动更新记录。

DROP TABLE emp_tax PURGE ;

CREATE TABLE emp_tax (

empno NUMBER(4) ,

ename VARCHAR2(10) ,

sal NUMBER(7,2) ,

comm NUMBER(7,2) ,

tax NUMBER(7,2) ,

CONSTRAINT pk_empno PRIMARY KEY (empno) ,

CONSTRAINT fk_empno FOREIGN KEY (empno) REFERENCES emp(empno) ON DELETE CASCADE

) ;

CREATE OR REPLACE TRIGGER forbid_emp_trigger

AFTER UPDATE OR INSERT OF ename , sal , comm

ON emp

DECLARE

PRAGMA AUTONOMOUS_TRANSACTION ; -- 触发器自治事务

CURSOR cur_emp IS SELECT * FROM emp ; -- 定义游标,找到每行纪录

v_empRow emp%ROWTYPE ; -- 保存emp的每行纪录

v_salary emp.sal%TYPE ; -- 计算总收入

v_empTax emp_tax.tax%TYPE ; -- 保存税收的数值

BEGIN

DELETE FROM emp_tax ; -- 清空emp_tax表的纪录

FOR v_empRow IN cur_emp LOOP

v_salary := v_empRow.sal + NVL(v_empRow.comm , 0) ; -- 计算总工资

IF v_salary < 2000 THEN

v_empTax := v_salary * 0.03 ; -- 上缴3%的税

ELSIF v_salary BETWEEN 2000 AND 5000 THEN

v_empTax := v_salary * 0.08 ; -- 上缴8%的税

ELSIF v_salary > 5000 THEN

v_empTax := v_salary * 0.1 ; -- 上缴10%的税

END IF ;

INSERT INTO emp_tax(empno,ename,sal,comm,tax) VALUES

(v_empRow.empno , v_empRow.ename , v_empRow.sal , v_empRow.comm , v_empTax) ;

END LOOP ;

COMMIT ; -- 提交事务

END ;

/

 

范例:emp表中增加一条新的记录,而后查询emp_tax表记录

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (9898,'MLDN','MANAGER',SYSDATE,2000,500,7369,40) ;

 

范例:增加完成后查询emp_tax表记录

SELECT * FROM emp_tax ;

 

 

范例:增加雇员信息时,其职位必须在已有职位之内选择,并且工资不能超过5000

CREATE OR REPLACE TRIGGER forbid_emp_trigger

BEFORE INSERT

ON EMP

FOR EACH ROW

DECLARE

v_jobCount NUMBER ;

BEGIN

SELECT COUNT(empno) INTO v_jobCount FROM emp WHERE :new.job IN (

SELECT DISTINCT job FROM emp) ;

IF v_jobCount = 0 THEN -- 没有此职位信息

RAISE_APPLICATION_ERROR(-20008,'增加雇员的职位信息名称错误!') ;

ELSE

IF :new.sal > 5000 THEN

RAISE_APPLICATION_ERROR(-20008,'增加雇员的工资不得超过5000') ;

END IF ;

END IF ;

END ;

/

 

范例:插入错误的数据

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (8998,'MLDN','经理',SYSDATE,9000,500,7369,40) ;

 

范例:修改emp表的基本工资涨幅不能超过10%

CREATE OR REPLACE TRIGGER emp_update_trigger

BEFORE UPDATE OF sal

ON emp

FOR EACH ROW

BEGIN

IF ABS((:new.sal-:old.sal)/:old.sal) > 0.1 THEN

RAISE_APPLICATION_ERROR(-20008,'雇员工资修改幅度太大!') ;

END IF ;

END;

/

 

范例:将雇员编号是7369的雇员工资增长为5000

UPDATE emp SET sal=5000 WHERE empno=7369 ;

 

范例:不能删除所有10部门的雇员

CREATE OR REPLACE TRIGGER emp_delete_trigger

BEFORE DELETE

ON emp

FOR EACH ROW

BEGIN

IF :old.deptno=10 THEN

RAISE_APPLICATION_ERROR(-20008,:old.empno || '10部门雇员,无法删除此部门雇员!') ;

END IF ;

END;

/

 

范例:删除雇员编号是7839的雇员信息(此雇员在10部门)

DELETE FROM emp WHERE empno=7839 ;

 

范例:错误的使用标识符:new”和“:old

CREATE OR REPLACE TRIGGER emp_error_trigger

BEFORE UPDATE

ON emp

FOR EACH ROW

DECLARE

v_empRow emp%ROWTYPE ;

BEGIN

v_empRow := :old ; -- 错误

END;

/

 

范例:错误的程序 —— 在触发器之中无法修改old数据

CREATE OR REPLACE TRIGGER emp_update_old_trigger

BEFORE UPDATE OF sal

ON emp

FOR EACH ROW

BEGIN

:old.sal := 100 ;  -- 错误,无法修改old数据

END;

/

 

范例:数据库创建脚本

DROP SEQUENCE member_sequence ;

DROP TABLE member PURGE ;

DROP TABLE membertemp PURGE ;

CREATE SEQUENCE member_sequence ;

CREATE TABLE member(

mid NUMBER ,

name VARCHAR2(30) ,

address VARCHAR2(50) ,

CONSTRAINT pk_mid PRIMARY KEY(mid)

) ;

CREATE TABLE membertemp AS SELECT * FROM member WHERE 1=2 ;

 

范例:在触发器之中修改new数据

CREATE OR REPLACE TRIGGER member_insert_trigger

BEFORE INSERT

ON membertemp  

FOR EACH ROW

BEGIN

DELETE FROM membertemp ;

INSERT INTO member(mid,name,address) VALUES (member_sequence.NEXTVAL,:new.name ,:new.address ) ;

END;

/

 

范例:membertemp表中执行增加数据操作

INSERT INTO membertemp(name,address) VALUES ('李兴华','中国') ;

INSERT INTO membertemp(name,address) VALUES ('董  楠','MLDN') ;

COMMIT ;

 

范例:查询member表数据

SELECT * FROM member ;

 

范例:换种方式实现本程序的触发器

CREATE OR REPLACE TRIGGER member_insert_trigger

BEFORE INSERT

ON membertemp  

FOR EACH ROW

DECLARE

v_memberRow member%ROWTYPE ;

BEGIN

DELETE FROM membertemp ;

SELECT member_sequence.NEXTVAL INTO :new.mid FROM dual ;

v_memberRow.mid := :new.mid ;

v_memberRow.name := :new.name ;

v_memberRow.address := :new.address ;

INSERT INTO member VALUES v_memberRow ;

END;

/

 

 

范例:通过REFERENCING子句设置别名(修改雇员工资涨副触发器)

CREATE OR REPLACE TRIGGER emp_insert_trigger

BEFORE UPDATE OF sal

ON emp

REFERENCING old AS emp_old new AS emp_new

FOR EACH ROW

BEGIN

IF ABS((:emp_new.sal-:emp_old.sal)/:emp_old.sal) > 0.1 THEN

RAISE_APPLICATION_ERROR(-20008,'雇员工资涨副太大!') ;

END IF ;

END;

/

 

 

范例:在增加雇员时,判断雇员工资是否存在,如果工资为0则报错

CREATE OR REPLACE TRIGGER emp_insert_trigger

BEFORE INSERT

ON emp  

FOR EACH ROW

WHEN (new.sal = 0)

BEGIN

RAISE_APPLICATION_ERROR(-20008,:new.empno || '的工资为0,不符合工资规定!') ;

END;

/

 

范例:增加新雇员,工资为0 —— 不符合操作要求

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (8998,'MLDN','经理',SYSDATE,0,500,7369,40) ;

 

范例:要求工资只能上涨,不能降低

CREATE OR REPLACE TRIGGER emp_sal_update_trigger

BEFORE UPDATE

ON emp

FOR EACH ROW

WHEN (new.sal<old.sal)

BEGIN

RAISE_APPLICATION_ERROR(-20008,:old.empno || '的工资少于其原本工资,无法更新!') ;

END;

/

 

范例:7369的工资修改为300(原本为800,现在修改为300,属于降低工资,满足触发条件)

UPDATE emp SET sal=300 WHERE empno=7369 ;

 

范例:定义DEPT_LOG

DROP TABLE dept_log PURGE ;

DROP SEQUENCE dept_log_seq ;

CREATE SEQUENCE dept_log_seq ;

CREATE TABLE dept_log (

logid NUMBER ,

type VARCHAR2(20) NOT NULL ,

deptno NUMBER(2) ,

logdate DATE ,

dname VARCHAR2(14) NOT NULL ,

loc VARCHAR2(13) NOT NULL ,

CONSTRAINT pk_logid PRIMARY KEY (logid)

) ;

 

 

范例:定义触发器,针对于不同的DML操作进行日志记录

CREATE OR REPLACE TRIGGER dept_update_trigger

BEFORE INSERT OR UPDATE OR DELETE

ON dept

FOR EACH ROW

BEGIN

IF INSERTING THEN

INSERT INTO dept_log (logid,type,logdate,deptno,dname,loc)

VALUES (dept_log_seq.nextval,'INSERT',SYSDATE,:new.deptno,:new.dname,:new.loc) ;

ELSIF UPDATING THEN

INSERT INTO dept_log (logid,type,logdate,deptno,dname,loc)

VALUES (dept_log_seq.nextval,'UPDATE',SYSDATE,:new.deptno,:new.dname,:new.loc) ;

ELSE -- 相当于DELETING

INSERT INTO dept_log (logid,type,logdate,deptno,dname,loc)

VALUES (dept_log_seq.nextval,'DELETE',SYSDATE,:old.deptno,:old.dname,:old.loc) ;

END IF ;

END;

/

 

 

范例:dept表中执行一些DML操作

INSERT INTO dept(deptno,dname,loc) VALUES (50,'MLDN','北京') ;

INSERT INTO dept(deptno,dname,loc) VALUES (60,'教学部','天津') ;

UPDATE dept SET dname='北京' WHERE deptno=60 ;

UPDATE dept SET dname='MLDNJAVA' WHERE deptno=50 ;

DELETE FROM dept WHERE deptno=60 ;

COMMIT ;

 

范例:查询dept_log数据表

SELECT * FROM dept_log ;

 

范例:定义三个针对于增加操作的触发器

CREATE OR REPLACE TRIGGER emp_insert_one

BEFORE INSERT

ON emp

FOR EACH ROW

BEGIN

DBMS_OUTPUT.put_line('执行第1个触发器(emp_insert_one') ;

END ;

/

CREATE OR REPLACE TRIGGER emp_insert_two

BEFORE INSERT

ON emp

FOR EACH ROW

BEGIN

DBMS_OUTPUT.put_line('执行第2个触发器(emp_insert_two') ;

END ;

/

CREATE OR REPLACE TRIGGER emp_insert_three

BEFORE INSERT

ON emp

FOR EACH ROW

BEGIN

DBMS_OUTPUT.put_line('执行第3个触发器(emp_insert_three') ;

END ;

/

 

范例:编写增加数据操作

INSERT INTO emp (empno,ename,job,hiredate,sal,comm,mgr,deptno)

VALUES (8998,'MLDN','经理',SYSDATE,0,500,7369,40) ;

 

范例:修改触发器创建语法

CREATE OR REPLACE TRIGGER emp_insert_one

BEFORE INSERT

ON emp

FOR EACH ROW

BEGIN

DBMS_OUTPUT.put_line('执行第1个触发器(emp_insert_one') ;

END ;

/

CREATE OR REPLACE TRIGGER emp_insert_two

BEFORE INSERT

ON emp

FOR EACH ROW

FOLLOWS emp_insert_one

BEGIN

DBMS_OUTPUT.put_line('执行第2个触发器(emp_insert_two') ;

END ;

/

CREATE OR REPLACE TRIGGER emp_insert_three

BEFORE INSERT

ON emp

FOR EACH ROW

FOLLOWS emp_insert_two

BEGIN

DBMS_OUTPUT.put_line('执行第3个触发器(emp_insert_three') ;

END ;

/

 

范例:定义一张数据表

DROP TABLE info PURGE ;

CREATE TABLE info(

id NUMBER ,

title VARCHAR2(50)

) ;

INSERT INTO info (id,title) VALUES (1,'www.mldnjava.cn') ; 

 

范例:info表增加一个触发器

CREATE OR REPLACE TRIGGER info_trigger

BEFORE INSERT OR UPDATE OR DELETE

ON info

FOR EACH ROW

DECLARE

v_infocount NUMBER ;

BEGIN

SELECT COUNT(id) INTO v_infocount FROM info ;

END ;

/

 

范例:执行更新操作

UPDATE info SET id=2 ;

 

 

范例:验证复合触发器

CREATE OR REPLACE TRIGGER compound_trigger

FOR INSERT OR UPDATE OR DELETE ON dept

COMPOUND TRIGGER

BEFORE STATEMENT IS-- 语句执行前触发(表级)

BEGIN

DBMS_OUTPUT.put_line('1BEFORE STATEMENT .') ;

END BEFORE STATEMENT ;

BEFORE EACH ROW IS-- 语句执行前触发(行级)

BEGIN

DBMS_OUTPUT.put_line('2BEFORE EACH ROW .') ;

END BEFORE EACH ROW ;

AFTER STATEMENT IS-- 语句执行后触发(表级)

BEGIN

DBMS_OUTPUT.put_line('3AFTER STATEMENT .') ;

END AFTER STATEMENT ;

AFTER EACH ROW IS-- 语句执行后触发(行级)

BEGIN

DBMS_OUTPUT.put_line('4AFTER EACH ROW .') ;

END AFTER EACH ROW ;

END ;

/

 

范例:dept表中增加一条新数据

INSERT INTO dept(deptno,dname,loc) VALUES (99,'MLDNJAVA','北京') ;

 

范例:定义复合触发器

CREATE OR REPLACE TRIGGER compound_trigger

FOR INSERT OR UPDATE OR DELETE ON dept

COMPOUND TRIGGER

BEFORE EACH ROW IS -- 语句执行前触发(行级)

BEGIN

IF INSERTING THEN

IF :new.dname IS NULL THEN

:new.dname := 'MLDNJAVA' ;

END IF ;

IF :new.loc IS NULL THEN

:new.loc := '中国' ;

END IF ;

END IF ;

END BEFORE EACH ROW ;

END ;

/

 

范例:增加一条部门信息

INSERT INTO dept(deptno) VALUES (99) ;

COMMIT ;

 

范例:查询更新后的dept表记录

SELECT * FROM dept ;

 

范例:定义触发器

CREATE OR REPLACE TRIGGER emp_compound_trigger

FOR INSERT OR UPDATE OR DELETE ON emp

COMPOUND TRIGGER

BEFORE STATEMENT IS -- 周末不允许更新

v_currentweak VARCHAR2(20) ;

BEGIN

SELECT TO_CHAR(SYSDATE,'day') INTO v_currentweak FROM dual ;

IF TRIM(v_currentweak) IN ('星期六' , '星期日') THEN

RAISE_APPLICATION_ERROR(-20008,'在周末不允许更新emp数据表!') ;

END IF ;

END BEFORE STATEMENT ;

BEFORE EACH ROW IS

v_avgSal emp.sal%TYPE ;

BEGIN

IF INSERTING OR UPDATING THEN

:new.ename := UPPER(:new.ename) ;

:new.job := UPPER(:new.job) ;

END IF ;

IF INSERTING THEN

SELECT AVG(sal) INTO v_avgSal FROM emp ;

IF :new.sal > v_avgSal THEN

RAISE_APPLICATION_ERROR(-20009,'新进雇员工资不得高于公司平均工资!') ;

END IF ;

END IF ;

END BEFORE EACH ROW ;

END ;

/

 

范例:向雇员表中增加一条正确的数据

INSERT INTO emp (empno,ename,job,mgr,sal,comm,deptno,hiredate)

VALUES (9999,'mldn','manager',7566,1680,null,20,SYSDATE) ;

COMMIT ;

 

范例:查询emp表中9999雇员信息

SELECT * FROM emp WHERE empno=9999 ;

 

范例:向雇员表增加一条雇员信息,工资为5000(已经超过了平均工资)

INSERT INTO emp (empno,ename,job,mgr,sal,comm,deptno,hiredate)

VALUES (8888,'lixinghua','manager',7566,5000,null,20,SYSDATE) ;

 

范例:创建一张包含20部门雇员编号、姓名、职位、基本工资、部门编号、部门名称、位置的视图

CREATE OR REPLACE VIEW v_myview AS

SELECT e.empno , e.ename , e.job , e.sal , d.deptno , d.dname , d.loc

FROM emp e,dept d

WHERE e.deptno=d.deptno AND d.deptno=20 ;

 

范例:向视图插入一条数据

INSERT INTO v_myview (empno , ename, job , sal , deptno,dname,loc)

VALUES (6688, '魔乐' , 'CLERK' , 2000, 50 , '教学' , '北京') ;

 

范例:创建一个INSERT替代触发器,用于执行视图更新操作

CREATE OR REPLACE TRIGGER view_trigger

INSTEAD OF INSERT ON v_myview

FOR EACH ROW

DECLARE

v_empCount NUMBER ;

v_deptCount NUMBER ;

BEGIN

-- 判断要增加的雇员是否存在

SELECT COUNT(empno) INTO v_empCount FROM emp WHERE empno=:new.empno ;

-- 判断要增加的部门是否存在

SELECT COUNT(deptno) INTO v_deptCount FROM dept WHERE deptno=:new.deptno ;

IF v_deptCount = 0 THEN -- 部门不存在

INSERT INTO dept(deptno,dname,loc) VALUES (:new.deptno , :new.dname , :new.loc) ;

END IF ;

IF v_empCount = 0 THEN

INSERT INTO emp(empno,ename,job,sal,deptno)

VALUES (:new.empno , :new.ename , :new.job , :new.sal , :new.deptno) ;

END IF ;

END ;

/

 

范例:执行视图增加操作

INSERT INTO v_myview (empno , ename, job , sal , deptno,dname,loc)

VALUES (6688, '魔乐' , 'CLERK' , 2000, 50 , '教学' , '北京') ;

COMMIT ;

 

范例:查询emp表中是否存在了新增的6688记录

SELECT * FROM emp WHERE empno=6688 ;

 

范例:查询dept表中是否存在了新增的50部门记录

SELECT * FROM dept WHERE deptno=50 ;

 

范例:创建一个UPDATE替代触发器

CREATE OR REPLACE TRIGGER view_trigger

INSTEAD OF UPDATE ON v_myview

FOR EACH ROW

BEGIN

UPDATE emp SET ename=:new.empno , job=:new.job , sal=:new.sal WHERE empno=:new.empno ;

UPDATE dept SET dname=:new.dname,loc=:new.loc WHERE deptno=:new.deptno ;

END ;

/

 

范例:更新视图信息

UPDATE v_myview SET ename='史密思',sal=2000,dname='调研部' WHERE empno=7369 ;

COMMIT ;

 

范例:查询v_myview视图

SELECT * FROM v_myview ;

 

范例:查询emp表中7369雇员信息是否被更新

SELECT * FROM emp WHERE empno=7369 ;

 

范例:查询dept表中20部门的信息是否被更新

SELECT * FROM dept WHERE deptno=20 ;

 

范例:创建一个DELETE替代触发器

CREATE OR REPLACE TRIGGER view_trigger

INSTEAD OF DELETE ON v_myview

FOR EACH ROW

DECLARE

v_empCount NUMBER ;

BEGIN

DELETE FROM emp WHERE empno=:old.empno ;

SELECT COUNT(empno) INTO v_empCount FROM emp WHERE deptno=:old.deptno ;

IF v_empCount = 0 THEN -- 此部门没有雇员

DELETE FROM dept WHERE deptno=:old.deptno ;

END IF ;

END ;

/

 

范例:删除视图之中所有20部门的雇员

DELETE FROM v_myview WHERE deptno=20 ;

COMMIT ;

 

范例:将三个不同功能的替代触发器变为一个替代触发器

CREATE OR REPLACE TRIGGER view_trigger

INSTEAD OF INSERT OR UPDATE OR DELETE ON v_myview

FOR EACH ROW

DECLARE

v_empCount NUMBER ;

v_deptCount NUMBER ;

BEGIN

IF INSERTING THEN

-- 判断要增加的雇员是否存在

SELECT COUNT(empno) INTO v_empCount FROM emp WHERE empno=:new.empno ;

-- 判断要增加的部门是否存在

SELECT COUNT(deptno) INTO v_deptCount FROM dept WHERE deptno=:new.deptno ;

IF v_deptCount = 0 THEN -- 部门不存在

INSERT INTO dept(deptno,dname,loc)

VALUES (:new.deptno , :new.dname , :new.loc) ;

END IF ;

IF v_empCount = 0 THEN

INSERT INTO emp(empno,ename,job,sal,deptno)

VALUES (:new.empno , :new.ename , :new.job , :new.sal , :new.deptno) ;

END IF ;

ELSIF UPDATING THEN

UPDATE emp SET ename=:new.empno , job=:new.job , sal=:new.sal WHERE empno=:new.empno ;

UPDATE dept SET dname=:new.dname,loc=:new.loc WHERE deptno=:new.deptno ;

ELSIF DELETING THEN

DELETE FROM emp WHERE empno=:old.empno ;

SELECT COUNT(empno) INTO v_empCount FROM emp WHERE deptno=:old.deptno ;

IF v_empCount = 0 THEN -- 此部门没有雇员

DELETE FROM dept WHERE deptno=:old.deptno ;

END IF ;

ELSE

NULL ;

END IF ;

END ;

/

 

范例:定义嵌套表

-- 1、定义复合类型

DROP TYPE project_nested ;

DROP TYPE project_type ;

CREATE OR REPLACE TYPE project_type AS OBJECT(

projectid NUMBER ,

projectname VARCHAR2(50) ,

projectfunds NUMBER ,

pubdate DATE

) ;

/

-- 2、定义嵌套表类型

CREATE OR REPLACE TYPE project_nested AS TABLE OF project_type NOT NULL ;

/

-- 3、创建嵌套表类型的数据表

DROP TABLE department PURGE ;

CREATE TABLE department (

did NUMBER ,

deptname VARCHAR2(50) NOT NULL ,

projects project_nested ,

CONSTRAINT pk_did PRIMARY KEY(did)

) NESTED TABLE projects STORE AS projects_nsted_table ;

-- 4、增加测试数据

INSERT INTO department(did,deptname,projects) VALUES (10,'魔乐科技' ,

project_nested(

project_type(1,'Java实战开发' , 8900 , TO_DATE('2004-09-27','yyyy-mm-dd')),

project_type(2,'Android实战开发' , 13900 ,TO_DATE('2010-07-19','yyyy-mm-dd'))

)) ;

INSERT INTO department(did,deptname,projects) VALUES (20,'MLDN出版部' ,

project_nested(

project_type(10,'Java开发实战经典》' , 79.8 , TO_DATE('2008-08-13','yyyy-mm-dd')) ,

project_type(11,'Java Web开发实战经典》' , 69.8 , TO_DATE('2010-08-27','yyyy-mm-dd')) ,

project_type(12,'Android开发实战经典》', 88 , TO_DATE('2012-03-19','yyyy-mm-dd'))

)) ;

COMMIT ;

 

范例:创建一张只包含10部门信息的视图,在此视图之中存在嵌套表类型projects列。

CREATE OR REPLACE VIEW v_department10

AS

SELECT did,deptname,projects

FROM department

WHERE did=10 ;

 

范例:查看视图中的嵌套表数据

SELECT * FROM TABLE (SELECT projects FROM v_department10) ;

 

范例:对视图中的嵌套表执行增加数据操作 —— 错误

INSERT INTO TABLE (SELECT projects FROM v_department10)

VALUES (3,'Java高端人才培养',8000,TO_DATE('2013-09-19','yyyy-mm-dd')) ;

 

范例:对视图中的嵌套表执行修改数据操作 —— 错误

UPDATE TABLE (SELECT projects FROM v_department10) pro

SET VALUE(pro) = project_type(2,'Android高级应用',3000,TO_DATE('2013-06-06','yyyy-mm-dd'))

WHERE pro.projectid=2 ;

 

范例:对视图中的嵌套表执行删除数据操作 —— 错误

DELETE FROM TABLE(

SELECT projects FROM v_department10) pro

WHERE pro.projectid=2 ;

 

范例:定义替代触发器实现对视图中的嵌套表数据更新

CREATE OR REPLACE TRIGGER nested_trigger

INSTEAD OF INSERT OR UPDATE OR DELETE

ON NESTED TABLE projects OF v_department10

DECLARE

BEGIN

IF INSERTING THEN

INSERT INTO TABLE (SELECT projects FROM department WHERE did=:parent.did)

VALUES (:new.projectid,:new.projectname,:new.projectfunds,:new.pubdate) ;

ELSIF UPDATING THEN

UPDATE TABLE (SELECT projects FROM department WHERE did=:parent.did) pro

SET VALUE(pro) = project_type(:new.projectid,:new.projectname,:new.projectfunds,:new.pubdate)

WHERE pro.projectid=:old.projectid ;

ELSIF DELETING THEN

DELETE FROM TABLE(

SELECT projects FROM department WHERE did=:parent.did) pro

WHERE pro.projectid=:old.projectid ;

END IF ;

END ;

/

 

范例:禁止c##scott用户的所有DDL操作

CREATE OR REPLACE TRIGGER scott_forbid_trigger

BEFORE DDL

ON SCHEMA

BEGIN

RAISE_APPLICATION_ERROR(-20007,'scott用户禁止使用任何的DDL操作!') ;

END ;

/

 

范例:创建一个序列

CREATE SEQUENCE mldn_seq ;

 

范例:数据库对象操作日志记录表创建脚本

DROP TABLE object_log PURGE ;

DROP SEQUENCE object_log_seq ;

CREATE SEQUENCE object_log_seq ;

CREATE TABLE object_log (

oid NUMBER CONSTRAINT pk_oid PRIMARY KEY,

username VARCHAR2(50) NOT NULL ,

operatedate DATE NOT NULL ,

objecttype VARCHAR2(50) NOT NULL ,

objectowner VARCHAR2(50) NOT NULL

) ;

 

范例:编写触发器实现对数据库对象操作的日志记录

CREATE OR REPLACE TRIGGER object_trigger

AFTER CREATE OR DROP OR ALTER

ON DATABASE

DECLARE

BEGIN

INSERT INTO c##scott.object_log(oid,username,operatedate,objecttype,objectowner) VALUES

   (c##scott.object_log_seq.nextval,ORA_LOGIN_USER,

    SYSDATE,ORA_DICT_OBJ_TYPE,ORA_DICT_OBJ_OWNER) ;

END ;

/

 

范例:禁止修改emp数据表的empno(主键)列和deptno(外键)列的定义结构

SELECT * FROM all_tab_columns

WHERE table_name='EMP' AND owner='C##SCOTT';

 

范例:c##scott用户中定义参数游标

CREATE OR REPLACE TRIGGER emp_alter_trigger

BEFORE ALTER

ON SCHEMA

DECLARE

-- 操作的所有者以及操作的表名称由外部传递

CURSOR emp_column_cur(p_tableOwner all_tab_columns.owner%TYPE , p_tableName all_tab_columns.table_name%TYPE) IS

SELECT column_name FROM all_tab_columns

WHERE owner=p_tableOwner AND table_name=p_tableName ;

BEGIN

IF ORA_DICT_OBJ_TYPE = 'TABLE' THEN -- 如果操作的是数据表

FOR empColumnRow IN emp_column_cur(ORA_DICT_OBJ_OWNER , ORA_DICT_OBJ_NAME) LOOP

IF ORA_IS_ALTER_COLUMN(empColumnRow.column_name) THEN

-- empno字段要被修改

IF empColumnRow.column_name = 'EMPNO' THEN

RAISE_APPLICATION_ERROR(-20007,'empno字段不允许修改!') ;

END IF ;

-- deptno字段要被修改

IF empColumnRow.column_name = 'DEPTNO' THEN

RAISE_APPLICATION_ERROR(-20008,'deptno字段不允许修改!') ;

END IF ;

END IF ;

IF ORA_IS_DROP_COLUMN(empColumnRow.column_name) THEN

-- empno字段要被删除

IF empColumnRow.column_name = 'EMPNO' THEN

RAISE_APPLICATION_ERROR(-20009,'empno字段不允许删除!') ;

END IF ;

-- deptno字段要被删除

IF empColumnRow.column_name = 'DEPTNO' THEN

RAISE_APPLICATION_ERROR(-20010,'deptno字段不允许删除!') ;

END IF ;

END IF ;

END LOOP ;

END IF ;

END ;

/

 

范例:修改empno字段

ALTER TABLE emp MODIFY (empno NUMBER(6)) ;

 

范例:删除deptno字段

ALTER TABLE emp DROP COLUMN deptno ;

 

范例:编写user_log数据表创建脚本

DROP SEQUENCE user_log_seq ;

DROP TABLE user_log PURGE ;

CREATE SEQUENCE user_log_seq ;

CREATE TABLE user_log (

logid NUMBER CONSTRAINT pk_logid PRIMARY KEY ,

username VARCHAR2(50) NOT NULL ,

logondate DATE ,

logoffdate DATE ,

ip VARCHAR2(20) ,

logtype VARCHAR2(20)

) ;

 

范例:监控用户登录触发器 —— logon_trigger

CREATE OR REPLACE TRIGGER logon_trigger

AFTER LOGON

ON DATABASE

BEGIN

INSERT INTO user_log(logid,username,logondate,ip,logtype)

VALUES (user_log_seq.nextval,ORA_LOGIN_USER,SYSDATE,ORA_CLIENT_IP_ADDRESS,'LOGON')  ;

END ;

/

 

范例:监控用户注销触发器 —— logoff_trigger

CREATE OR REPLACE TRIGGER logoff_trigger

BEFORE LOGOFF

ON DATABASE

BEGIN

INSERT INTO user_log(logid,username,logoffdate,ip,logtype)

VALUES (user_log_seq.nextval,ORA_LOGIN_USER,SYSDATE,ORA_CLIENT_IP_ADDRESS,'LOGOFF')  ;

END ;

/

 

范例:回到sys用户下,查询user_log数据表

CONN sys/change_on_install AS SYSDBA ;

SELECT * FROM user_log ;

 

范例:定义数据库事件记录表

DROP TABLE db_event_log PURGE ;

DROP SEQUENCE db_event_log_seq ;

CREATE SEQUENCE db_event_log_seq ;

CREATE TABLE db_event_log (

eventId NUMBER CONSTRAINT pk_eventid PRIMARY KEY ,

eventType VARCHAR2(50) NOT NULL ,

eventDate DATE NOT NULL ,

eventUser VARCHAR2(50) NOT NULL

) ;

 

范例:编写触发器 —— 启动之后触发

CREATE OR REPLACE TRIGGER startup_trigger

AFTER STARTUP

ON DATABASE

BEGIN

INSERT INTO db_event_log(eventId,eventType,eventDate,eventUser)

VALUES (db_event_log_seq.nextval,'STARTUP',SYSDATE,ORA_LOGIN_USER) ;

COMMIT ;

END ;

/

 

范例:编写触发器 —— 关闭之前触发

CREATE OR REPLACE TRIGGER shutdown_trigger

BEFORE SHUTDOWN

ON DATABASE

BEGIN

INSERT INTO db_event_log(eventId,eventType,eventDate,eventUser)

VALUES (db_event_log_seq.nextval,'SHUTDOWN',SYSDATE,ORA_LOGIN_USER) ;

COMMIT ;

END ;

/

 

范例:查询db_event_log数据表记录

SHUTDOWN ABORT

STARTUP

SELECT * FROM db_event_log ;

 

范例:创建一张记录错误信息的数据表 —— db_error

DROP SEQUENCE db_error_seq ;

DROP TABLE db_error PURGE ;

CREATE SEQUENCE db_error_seq ;

CREATE TABLE db_error (

eid NUMBER CONSTRAINT pk_eid PRIMARY KEY ,

username VARCHAR2(50) ,

errorDate DATE ,

dbname VARCHAR2(50) ,

content CLOB

) ;

 

范例:定义数据库错误触发器

CREATE OR REPLACE TRIGGER error_trigger

AFTER SERVERERROR ON DATABASE

BEGIN

INSERT INTO db_error(eid,username,errorDate,dbname,content)

VALUES (db_error_seq.nextval,ORA_LOGIN_USER,SYSDATE,ORA_DATABASE_NAME,

DBMS_UTILITY.format_error_stack) ;

END ;

/

 

范例:使用c##scott用户登录,查看user_triggers数据字典

SELECT trigger_name,status,trigger_type,status,table_name,triggering_event,trigger_body FROM user_triggers ;

 

范例:emp_alter_trigger触发器修改为禁用状态

ALTER TRIGGER emp_alter_trigger DISABLE ;

 

范例:查询数据字典表,确定触发器状态

SELECT trigger_name,status FROM user_triggers ;

 

范例:禁用触发器代码结构

CREATE OR REPLACE TRIGGER emp_update_trigger

BEFORE INSERT OR UPDATE OR DELETE

ON dept

DISABLE

FOR EACH ROW

BEGIN

NULL ;

END;

/

 

范例:启用emp表的全部触发器

ALTER TABLE emp ENABLE ALL TRIGGERS;

 

范例:删除forbid_emp_trigger触发器

DROP TRIGGER emp_alter_trigger ;

 

范例:在每月10号允许办理新近人员入职,同时入职的新雇员工资不能够超过公司的平均工资

CREATE OR REPLACE PROCEDURE emp_update_date_proc

AS

v_currentdate VARCHAR2(20) ;

BEGIN

SELECT TO_CHAR(SYSDATE,'dd') INTO v_currentdate FROM dual ;

IF TRIM(v_currentdate)!='10' THEN

RAISE_APPLICATION_ERROR(-20008,'在每月的10号才允许办理入职手续!') ;

END IF ;

END;

/

CREATE OR REPLACE FUNCTION emp_avg_sal

RETURN NUMBER

AS

v_avg_salary emp.sal%TYPE ;

BEGIN

SELECT AVG(sal) INTO v_avg_salary FROM emp ;

RETURN v_avg_salary ;

END;

/

CREATE OR REPLACE TRIGGER forbid_emp_trigger

BEFORE INSERT

ON emp

FOR EACH ROW

BEGIN

emp_update_date_proc ; -- 调用过程

IF :new.sal>emp_avg_sal() THEN -- 调用函数

RAISE_APPLICATION_ERROR(-20009,'新进雇员工资不得高于公司平均工资!') ;

END IF ;

END ;

/

posted on 2016-08-24 19:51  肥宅兜  阅读(1501)  评论(0编辑  收藏  举报