Oracle入门(十四.21)之创建DML触发器:第二部分

一、使用条件谓词

在上文中,看到了一个触发器,可以防止在周末插入EMPLOYEES:

CREATE OR REPLACE TRIGGER secure_emp
BEFORE INSERT ON employees BEGINIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN') THENRAISE_APPLICATION_ERROR(-20500,'You may insert into EMPLOYEES'||' table only during business hours');END IF;
END;

假设希望在周末期间阻止EMPLOYEES上的任何DML操作,并为INSERT,UPDATE和DELETE提供不同的错误消息。 可以创建三个单独的触发器; 不过,也可以用一个触发器来做到这一点。

CREATE OR REPLACE TRIGGER secure_emp
BEFORE INSERT OR UPDATE OR DELETE ON employees
BEGINIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN') THENIF DELETING THEN RAISE_APPLICATION_ERROR(-20501,'You may delete from EMPLOYEES'||' table only during business hours');ELSIF INSERTING THEN RAISE_APPLICATION_ERROR(-20502,'You may insert into EMPLOYEES'||' table only during business hours');ELSIF UPDATING THEN RAISE_APPLICATION_ERROR(-20503,'You may update EMPLOYEES'||' table only during business hours');END IF;
END IF;
END;

可以使用条件谓词来测试特定列上的UPDATE:

CREATE OR REPLACE TRIGGER secure_emp
BEFORE UPDATE ON employees
BEGINIF UPDATING('SALARY') THENIF TO_CHAR(SYSDATE,'DY') IN ('SAT','SUN')THEN RAISE_APPLICATION_ERROR(-20501,'You may update SALARY'||' only during business hours');END IF;ELSIF UPDATING('JOB_ID') THENIF TO_CHAR(SYSDATE,'DY') = 'SUN'THEN RAISE_APPLICATION_ERROR(-20502,'You may not update JOB_ID on Sunday');END IF;
END IF;
END;


二、了解行触发器

请记住,对于每个触发的DML语句,语句触发器只执行一次:

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees BEGIN
INSERT INTO log_emp_table (who, when)VALUES (USER, SYSDATE);
END;
无论触发语句是更新一个员工,几个员工,还是根本没有员工,该触发器都会在日志表中正好插入一行。

假设您想为每个更新的员工在日志表中插入一行。 例如,如果更新了四名员工,则将四行插入日志表中。 你需要一个行触发器。

(1)行触发器触发序列

对于受触发DML语句影响的每一行,行触发器都会触发(执行)一次,无论是在处理该行之前还是仅在AFTER之后。 如果五个员工在部门50中,则行触发器执行五次:

UPDATE employeesSET salary = salary * 1.1WHERE department_id = 50;

(2)创建行触发器

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
INSERT INTO log_emp_table (who, when)VALUES (USER, SYSDATE);
END;

    可以使用FOR EACH ROW来指定行触发器。 上一张幻灯片中的UPDATE语句现在将五行插入日志表中,每个EMPLOYEE行更新一行。但是,日志表中的所有五行都是相同的。 日志表不显示哪些员工已更新,或者他们的薪水有哪些变化。

(3)使用:OLD和:NEW限定符

    只有在行触发器中,您是否可以在当前正在更新的EMPLOYEES行中引用和使用旧列和新列值。
    代码:OLD.column_name引用preupdate值,以及:NEW.column_name引用更新后的值。

例如,如果UPDATE语句将雇员的工资从10000更改为11000,则:OLD.salary的值为10000,以及:NEW.salary的值为11000.现可以将所需的数据插入日志记录表。

要记录employee_id,无论是否编码:OLD.employee_id或:NEW.employee_id,都无关紧要吗?有区别吗?

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
INSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary);
END;

行触发器的第二个例子

CREATE OR REPLACE TRIGGER audit_emp_values
AFTER DELETE OR INSERT OR UPDATE ON employees
FOR EACH ROW
BEGININSERT INTO audit_emp(user_name, time_stamp, id,old_last_name, new_last_name, old_title,new_title, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.last_name, :NEW.last_name, :OLD.job_id,:NEW.job_id, :OLD.salary, :NEW.salary);
END;

第二个示例:测试audit_emp_values触发器

INSERT INTO employees
(employee_id, last_name, job_id, salary, ...)VALUES (999, 'Temp emp', 'SA_REP', 1000,...);UPDATE employees
SET salary = 2000, last_name = 'Smith'
WHERE employee_id = 999;
SELECT user_name, time_stamp, ...FROM audit_emp;

行触发器的第三个例子

假设你需要防止不是总裁或副总裁的雇员的工资超过15000美元。

CREATE OR REPLACE TRIGGER restrict_salary
BEFORE INSERT OR UPDATE OF salary ON employees
FOR EACH ROW
BEGINIF NOT (:NEW.job_id IN ('AD_PRES', 'AD_VP'))AND :NEW.salary > 15000 THENRAISE_APPLICATION_ERROR (-20202,'Employee cannot earn more than $15,000.');END IF;
END;

测试restrict_salary触发器:

UPDATE employees SET salary = 15500WHERE last_name IN ('King','Davies');
King是(副)主席,但Davies不是。 此UPDATE语句会产生以下错误:
ORA-20202: Employee cannot earn more than $15,000.
ORA-06512: at “USVA_TEST_SQL01_T01.RESTRICT_SALARY”, line 4
ORA-04088: error during execution of trigger
‘USVA_TEST_SQL01_T01.RESTRICT_SALARY’
2. WHERE last_name IN (‘King’, ‘Davies’);

EMPLOYEES行都不会更新,因为UPDATE语句必须完全成功或根本不成功。


第四个例子:用触发器实现完整性约束

EMPLOYEES表在DEPARTMENTS表的DEPARTMENT_ID列中具有外键约束。 DEPARTMENT_ID 999不存在,因此此DML语句违反了约束条件,员工行未更新:

UPDATE employees SET department_id = 999
WHERE employee_id = 124; 

可以使用触发器自动创建新部门。 


第四个例子:创建触发器:

CREATE OR REPLACE TRIGGER employee_dept_fk_trg
BEFORE UPDATE OF department_id ON employees
FOR EACH ROW
DECLAREv_dept_id departments.department_id%TYPE;
BEGINSELECT department_id INTO v_dept_id FROM departmentsWHERE department_id = :NEW.department_id;
EXCEPTIONWHEN NO_DATA_FOUND THENINSERT INTO departments VALUES(:NEW.department_id,'Dept '||:NEW.department_id, NULL, NULL);

来测试它:

UPDATE employees SET department_id = 999
WHERE employee_id = 124;
-- Successful after trigger is fired

三、使用REFERENCING子句

再看一下行触发器的第一个例子:
CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary);
END;

如果EMPLOYEES表的名称不同,该怎么办?
如果它被称为OLD呢? OLD不是一个好名字,但是可能的。 我们的代码现在会是什么样子?

OLD现在意味着两件事:它是一个值限定符(如:NEW),也是一个表名。 该代码将起作用,但会令人困惑。 我们不需要

使用:OLD和:NEW。 我们可以通过包含REFERENCING子句来使用不同的限定符。

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON old
REFERENCING OLD AS former NEW AS latter
FOR EACH ROW
BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :former.employee_id,:former.salary, :latter.salary);
END;

FORMER和LATTER被称为关联名称。 他们是OLD和NEW的别名。 我们可以选择任何我们喜欢的相关名称(例如TOM和MARY),只要它们不是保留字。 REFERENCING子句只能用于行触发器。


四、使用WHEN子句

看看这个触发代码。 只有在新薪水高于旧薪水时才会记录薪资变化。
CREATE OR REPLACE TRIGGER restrict_salary
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGINIF :NEW.salary > :OLD.salary THENINSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary);END IF;
END;
整个触发器主体是一个单一的IF语句。 在现实生活中,这可能是许多代码行,包括CASE语句,循环和许多其他构造。 这将很难阅读。

可以在触发器标题中编写我们的IF条件,就在BEGIN子句之前。

CREATE OR REPLACE TRIGGER restrict_salary
AFTER UPDATE OF salary ON employees
FOR EACH ROW
WHEN (NEW.salary > OLD.salary)
BEGININSERT INTO log_emp_table(who, when, which_employee, old_salary, new_salary)VALUES (USER, SYSDATE, :OLD.employee_id,:OLD.salary, :NEW.salary);
END;

这段代码更容易阅读,特别是如果触发器体长且复杂。 WHEN子句只能用于行触发器。


五、INSTEAD OF触发器

复杂视图(例如基于联接的视图)无法更新。 假设EMP_DETAILS视图是基于EMPLOYEES和DEPARTMENTS联合的复杂视图。 以下SQL语句失败:
INSERT INTO emp_detailsVALUES (9001,'ABBOTT',3000, 10, 'Administration');

可以通过创建一个触发器来直接更新两个基表,而不是尝试(和失败)更新视图。

INSTEAD OF触发器总是行触发器。


(1)一个INSTEAD OF触发器的例子

将INSERT执行到基于NEW_EMPS和NEW_DEPTS表的EMP_DETAILS视图中:

INSERT INTO emp_detailsVALUES (9001,'ABBOTT',3000, 10, 'Administration');

(2)创建一个INSTEAD OF触发器

步骤1:创建表格和复杂视图:

CREATE TABLE new_emps AS
SELECT employee_id,last_name,salary,department_idFROM employees;
CREATE TABLE new_depts AS
SELECT d.department_id,d.department_name,sum(e.salary) dept_salFROM employees e, departments dWHERE e.department_id = d.department_idGROUP BY d.department_id,d.department_name;
CREATE VIEW emp_details AS
SELECT e.employee_id, e.last_name, e.salary,e.department_id, d.department_nameFROM new_emps e, new_depts dWHERE e.department_id = d.department_id; 

第2步:创建INSTEAD OF触发器:

CREATE OR REPLACE TRIGGER new_emp_dept
INSTEAD OF INSERT ON emp_details
BEGININSERT INTO new_empsVALUES (:NEW.employee_id, :NEW.last_name,:NEW.salary, :NEW.department_id);UPDATE new_deptsSET dept_sal = dept_sal + :NEW.salaryWHERE department_id = :NEW.department_id;
END;

(3)行触发器重访

看看这个行触发器,记录员工的工资变化:

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
BEGIN
INSERT INTO log_table(employee_id, change_date, salary)VALUES (:OLD.employee_id, SYSDATE, :NEW.salary);
END;

如果有一百万名员工,并且你给每个员工5%的工资增长:

UPDATE employees SET salary = salary * 1.05;

行触发器将自动执行一百万次,每次插入一行。 这将非常缓慢。


在课程的前期,学习了如何使用批量绑定(FORALL)来加速DML。 我们可以在我们的触发器中使用FORALL吗?

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp;
BEGIN... Populate log_emp_tab with employees’ change data
FORALL i IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i);
END;

这不起作用。 为什么不? 提示:请记住,这是一个行触发器,并考虑LOG_EMP_TAB收集变量的作用域。

CREATE OR REPLACE TRIGGER log_emps
AFTER UPDATE OF salary ON employees
FOR EACH ROW
DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp;
BEGIN... Populate log_emp_tab with employees’ change data
FORALL i IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i);
END;

在触发器的每次执行结束时,触发器变量会丢失范围。 所以每次触发行触发器时,LOG_EMP_TAB中已收集的所有数据都将丢失。为了避免丢失这些数据,我们需要一个只触发一次的触发器 - 一个语句触发器。 但要引用每行的列值(使用:OLD和:NEW),我们需要一个行触发器。

但是单个触发器不能同时是行触发器和语句触发器。 对? 错误! 我们创建一个复合触发器。


六、什么是复合触发器?

一个触发器,可以包含针对每个可能的时间点的操作:触发语句之前,每行之前,每行之后,触发语句之后。 复合触发器有一个声明部分,以及每个时间点的部分。 你不必包含所有的时间点,只需要你需要的时间点。 复合触发器变量的范围是整个触发器,因此它们在整个执行过程中保留其范围。

(1)复合触发结构


(2)例子

这个例子有一个声明部分和四个可能的时间点部分中的两个。


(3)完整代码

CREATE OR REPLACE TRIGGER log_emps
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
DECLARETYPE t_log_emp IS TABLE OF log_table%ROWTYPEINDEX BY BINARY_INTEGER;log_emp_tab t_log_emp;v_index BINARY_INTEGER := 0;
AFTER EACH ROW IS BEGINv_index := v_index + 1;log_emp_tab(v_index).employee_id := :OLD.employee_id;log_emp_tab(v_index).change_date := SYSDATE;log_emp_tab(v_index).salary := :NEW.salary;
END AFTER EACH ROW;
AFTER STATEMENT IS BEGIN
FORALL I IN log_emp_tab.FIRST..log_emp_tab.LASTINSERT INTO log_table VALUES log_emp_tab(i);
END AFTER STATEMENT;
END log_emps;




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/329313.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【直播预告】创享未来 2016微软开发者峰会

感谢所有中国开发者对2016微软开发者峰会的热情关注,目前活动已经截止报名了,不过M姐为大家带来新的福利: 2016微软开发者峰会将全程线上直播! 2016微软开发者峰会将全程线上直播! 2016微软开发者峰会将全程线上直播…

定时任务重启后执行策略_C语言操作时间函数time.ctime,实现定时执行某个任务小例子...

时间操作函数在实际项目开发中会经常用到,最近做项目也正好用到就正好顺便整理一下。时间概述由上图可知:通过系统调用函数time()可以从内核获得一个类型为time_t的1个值,该值叫calendar时间,即从1970年1月1日的UTC时间从0时0分0妙…

Oracle入门(十四.20)之创建DML触发器:第一部分

一、什么是DML触发器?DML触发器是执行SQL DML语句(INSERT,UPDATE或DELETE)时自动触发(执行)的触发器。 您可以通过两种方法对DML触发器进行分类: •执行时间:BEFORE,AFTE…

IIS负载均衡-Application Request Route详解第四篇:使用ARR实现三层部署架构

本篇的主要目的是带领大家一起来使用ARR来实现一个三层部署架构。这里的三层部署架构主要是由:服务层,应用程序服务器层已经数据层实现。如下图所示: 每次一提到“层”这个字的时候,似乎感觉这个字特别的惹火。很多朋友开始讨论起…

c遗传算法的终止条件一般_KDD比赛之遗传算法(举例理解)

求最大值问题是这样的:求解函数 f(x) x 10*sin(5*x) 7*cos(4*x) 在区间[0,9]的最大值。这个函数大概长这样:那么如何应用遗传算法如何来找到这个奇怪的函数的最大值呢?事实上,不管一个函数的形状多么奇怪,遗传算法都…

6.elasticsearch查询与过滤上下文(query context与filter contenxt)以及term术语查询

【README】 1.本文总结自: Query and filter context | Elasticsearch Guide [7.2] | Elastichttps://www.elastic.co/guide/en/elasticsearch/reference/7.2/query-filter-context.html2.文档相关性分数是否被计算,取决于查询子句是在查询上下文&…

Oracle入门(十四.22)之创建DDL和数据库事件触发器

一、什么是DDL和数据库事件触发器?DDL语句触发DDL触发器:CREATE,ALTER或DROP。 数据库事件触发器由数据库中的非SQL事件触发,例如: •用户连接到数据库或与数据库断开连接。 •DBA启动或关闭数据库。•用户会话中引发了…

Visual Studio上开发Python?你不可不知道的六大功能!

Visual Studio 2013/2015 搭配 Python Tools for Visual Studio 扩充套件让 Visual Studio 能提供对 Python 程序语言高度整合的开发环境,并完整发挥 Visual Studio 强大的功能,协助您在 Visual Studio 内开发 Python 程序上如虎添翼,提升开发…

qt中sendevent_Qt中postEvent和sendEvent函数

Qt中postEvent和sendEvent函数部分内容参考http://blog.csdn.net/lvmengzou/article/details/65450908qt事件循环需要维护一个事件队列,在Qt的main函数中最后一般调用QApplication::exec()成员函数来保持程序对事件队列的处理,exec()的实质是不停调用pro…

IIS负载均衡-Application Request Route详解第五篇:使用ARR来配置试点项目

看到本篇的题目,大家可能感到有点奇怪!下面,我们就来看看这到底是什么意思。 大家可能遇到过这样的一种情况:希望根据某些请求用户的特性,将用户的请求导向不同的站点(请大家这里区分“亲缘性”的概念&…

Oracle入门(十四.23)之管理触发器

一、触发器需要特权要在模式中创建触发器,需要: •CREATE TRIGGER系统特权 •触发器主体中引用的其他架构中的对象的普通对象特权(SELECT,UPDATE,EXECUTE等) •与触发器关联的表或视图上的ALTER特权。触发器…

为什么哲学是最难的学科_什么是哲学哲学对大师来说可能非常理论化,没有一定哲学基础肯能很难 爱问知识人...

我的总结是科学哲学是从哲学角度考察科学的一门学科。它以科学活动和科学理论为研究对象,探讨科学的本质、科学知识的获得和检验、科学的逻辑结构等有关科学认识论和科学方法论的基本问题。哲学是什么?这是一个问题,一个既简单又复杂的问题。…

8.es更新文档通过版本号实现并发控制

【README】 1.本文介绍了es更新文档时的并发控制策略;2.通过版本号实现并发控制(类似于mysql中基于版本号的乐观锁);3.Es为支持并发控制,为每篇文章设置了版本号_version。初始值为1,每更新1次加1。…

Oracle入门(十五)之数据库锁

一、锁的概念 锁是数据库用来控制共享资源并发访问的机制。锁用于保护正在被修改的数据直到提交或回滚了事务之后,其他用户才可以更新数据二、锁定的优点 一致性 - 一次只允许一个用户修改数据完整性 - 为所有用户提供正确的数据。如果一个用户进行了修改并保存&a…

.NET 和 Mono 的一点历史

提到微软公司研发 .NET Framework 的初衷,难免要提到 SUN 公司1995年推出的 Java 语言。由于 Java 在业界得到了广泛的支持而且迅速建立了庞大的生态系统,微软也不得不考虑如何加以应对,毕竟自己手里的 Visual Basic 和 Visual C 和 Java 一比…

lisp 角平分线_证明冯奥贝尔定理的3种方法

怎样证明冯奥贝尔定理?Von.Aubel定理: 以任意四边形ABCD的边为斜边作四个转向相同的等腰直角三角形ΔABE,ΔBCF,ΔCDG,ΔDAH。则:EGFH,EG⊥FH。关于上述定理的几点说明:(1),条件是任意四边形,所…

java迭代实现二叉树先中后序遍历(非递归)

【README】 本文复习了通过java迭代实现 二叉树先序,中序,后序遍历; 本文引入了 栈,替换了递归,对二叉树进行遍历; 补充:使用递归遍历二叉树缺点: 众所周知,每次递归…

漫画:什么是分布式锁

转载自 漫画:什么是分布式锁分布式锁的实现有哪些?1.Memcached分布式锁利用Memcached的add命令。此命令是原子性操作,只有在key不存在的情况下,才能add成功,也就意味着线程得到了锁。2.Redis分布式锁和Memcached的方式…

程序员求职面试三部曲之一:选择合适的工作单位

前不久在知乎上看到一个话题,大概是说中国比国外好的有哪些方面,网友们例举了一大堆,其中有一条是说“在中国找工作比较容易”。 是的,特别对于我们IT从业者来说,找工作真的是小菜一碟;只要肯在网络上公开简…

处理api返回的数据_API 乐队指挥家,网关服务正式上线

随着知晓云小伙伴们业务的发展,对我们服务的支持又有了新的要求,比如在对接第三方服务时,需要自定义数据结构等。为此,经过 32 次的需求讨论会以及工程师们的紧张开发后,知晓云 API 网关诞生了。你可以通过可视化界面配…