数据库原理及应用_数据库基础_第4章关系模型的基本理论_触发器
前言
"<数据库原理及应用>(MySQL版)".以下称为"本书"中4.2.3节内容
引入
回顾程序的组成:程序=数据结构+算法,这是经典定义.当将其细化,数据结构=数据类型的集合,则需要设计数据类型.而狭义上的算法是和数据结构相关,广义上来讲:算法=函数.程序的设计就是设计出一个个符合需要的数据类型,选择合适的数据结构,以及设计表达逻辑的函数.
函数相当于用数据来表达逻辑,函数名是需要满足的要求,函数体是数据的变化对逻辑的实现.函数可以调用其他函数,传入参数,实现嵌套.用高级语言操作数据库时,也是把参数传给SQL语句,实现逻辑返回数据.
问题是SQL语句没法像高级语言那样去嵌套其他SQL语句(本书没有例子,笔者也没有去试),所以SQL语句比较单一,只能提供"一层"操作.由于天然缺乏过程控制能力,所以触发器显得非常"宝贵",这也是单独一篇贴的原因.
概述
本书P111触发器黑体字下第一段内容(黑体字是原话)
1>常用和传统的触发器是定义在一个表上或一个简单视图(针对一个表的视图)上的触发器,称为DML触发器.
---触发器的定义的对象是一个表,也就是说可以给每个表设计触发器.
2>这种触发器由用户对数据库中的表操作(INSERT,UPDATE和DELETE这3种操作)触发
---此处复习一下基础SQL语句的内容
注意:本书描述的是对数据库中的表操作,实际上是对表数据的操作
概括一下,凡是对表内数据有修改的操作(除了查询外的操作),都可以用触发器.
3>由于触发器是由操作触发的过程,所以可以利用触发器来维护表间数据的一致性.
---触发器除了维护表间数据一致性,但他有更广的应用空间:可以监测数据的操作,建立日志.
AFTER触发器和BEFORE触发器
AFTER触发器表示先执行相应语句,再执行触发器中触发体内的语句
BEFORE触发器表示先执行触发体内语句,再执行相应语句.
AFTER触发器:不干扰执行,事后记录; BEFORE触发器可能会干扰执行语句.举个例子,如果给INSERT语句里增加BEFORE触发器,在触发器语句里再添加一个INSERT语句.那么每当添加一个元组(行),数据库里会多出一行数据(后面INSERT语句添加).因此对数据库的操作要有严格权限和管理.本书中并没有用BEFORE触发器写例子,可以尝试.
抽象理解
如图:触发器可以用作"条件"触发其他语句
=============================内容分割线↓===================================
以下内容属于思路并未实现
发散思考:上图可以作为一个要实现的需求,如果用高级语言来实现"触发器",该怎么做?
"修改语句"可以用函数指针.三个语句如果能统一成同一类型函数指针,就用1个,否则用3个.
"是否有触发器?"做1个触发器集合:可以用链表List或者vector,里面有最大6个元素.分别对应每个语句的BEFORE和AFTER(因为1个表对应1个触发器).
触发器类型设计:
//伪代码
class Trigger{string function_name; //insert,update或者delete的标识bool ba; //before或者after的判定void fun(); //可以表达所有函数
}
代码思路:先用函数指针去判断要执行的函数是什么,然后把名称换成string类型,进入数据结构中判断,是不是有触发器,有哪种触发器,后根据情况执行对应的函数.
其实并没有细想,而且中间未必能实现,比如函数指针转成string类型怎么做?所以看看就好(*^_^*)
=============================内容分割线↑===================================
触发器语法
CREATE TRIGGER 触发器名
BEFORE | AFTER
INSERT | DELETE | UPDATE
ON 表名
FOR EACH ROW
< 触发体 >
说明:触发体通常是DML语句,即insert,delete或者update,或可用DDL语句但不建议用(会改变表结构)
含义:当关于某个表(表名标识)的DML语句出现时,改变某些数据.
例4-7日志信息表
创建一个删除触发器,记录哪些用户删除了department表中的数据,以及删除的时间
1.创建merch_log的日志信息表,用于存储用户对表的操作
CREATE TABLE merch_log(who VARCHAR(30),oper_date DATE
)
2.创建触发器
CREATE TRIGGER delete_trigger
AFTER DELETE
ON department
FOR EACH NOW
INSERT INTO merch_log(who,oper_date) VALUES(USER(),SYSDATE());
3.测试
department里没有测试选项,所以先添加deptno=10的选项,如下图
执行删除语句
DELETE FROM department WHERE deptno=10;
运行结果
merch_log表查看
例4-8级联更新
目的:保持表间数据一致性
=============================内容分割线↓===================================
外键约束的补充说明:
在上一篇帖子中,带了外键约束的"ON UPDATE CASCADE",当更新主表的主键时,从表外键随之更新;当删除主表主键时,有两种情况:
1.如果从表内与之关联的外键还在,删除被禁止,也就是起到了"NO ACTION"的效果.
2.如果从表中没有和主表主键关联的外键,可以删除.也就是要先删除从表外键项,才能删除主表中对应主键项.
看起来CASCADE可以满足级联更新和删除的要求.
=============================内容分割线↑===================================
1.建立新表
为了看到效果,建立一个新表employee3,表的关系模式和employee2一样,但不添加外键约束,如下
CREATE TABLE employee3(empno DECIMAL(5) PRIMARY KEY,ename VARCHAR(10) NOT NULL,age DECIMAL(3),deptno DECIMAL(5)
);
2.添加数据
---这里有个不错的操作,在navicat premium lite17里,按住Ctrl可以选中多行,粘贴到新表中
前两行通过行粘贴操作.
3.创建触发器
/*级联更新触发器*/
CREATE TRIGGER tr_dept_emp
AFTER UPDATE
ON department
FOR EACH ROW
UPDATE employee3 SET deptno=NEW.deptno
WHERE deptno=OLD.deptno;
4.测试
把department的主键修改
查看employee3的内容
修改成功
5.是否有级联删除的限制
前面提到了ON UPDATE CASCADE可以保持表间数据一致性,禁止删除从表外键项,这里的操作能否实现同样功能呢?
添加一个"测试部",编号6666
更新employee3的数据
尝试删除department表中的"测试部"这一项,未见出错.
因此他没有级联删除的限制
级联删除
方案一
/*级联删除触发器*/
CREATE TRIGGER tr_dept_emp2
AFTER DELETE
ON department
FOR EACH ROW
UPDATE employee3 SET deptno=NULL
WHERE deptno=OLD.deptno;
这个触发器的意思是:当department执行某个项的删除,设置employee3中对应项为NULL
执行删除后,在上一张图重新添加"测试部"再删除,再次打开表employee3
方案二
/*级联删除触发器二*/
CREATE TRIGGER tr_dept_emp3
AFTER DELETE
ON department
FOR EACH ROW
DELETE FROM employee3
WHERE deptno=OLD.deptno;
这个触发器的意思是:如果department删除某个主键项,employee3中对应项所在元组(行)被删除
很遗憾,操作并未成功.但是仔细想一想,避免误删数据岂不是很好?
外键约束和触发器
笔者个人看法:在保持表间数据一致性上,外键约束强于触发器.很明显,外键约束是一种强相关,他能保证输入时从表外键的值在主键取值范围之内,也能避免主表主键的误删.
再看触发器
触发器是数据库管理员必备技能.他最大的优点是监控数据的变动.试想每一张从表的每个DML操作(INSERT,UPDATE和DELETE)都定义触发器,那么数据的所有变化情况都清清楚楚地被记录下来.因此他也是非常重要的.
小结
触发器的学习和操作.