触发器(Trigger)的起源
一、什么是触发器
触发器(trigger):监视某种情况,并触发执行某种操作。触发器是在表中数据发生更改时自动触发执行的,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,例如当对一个表进行操作(insert,delete, update)时就会激活它执行。也就是说触发器只执行DML事件(insert、update和delete)
1. 安全性。可以基于数据库的值使用户具有操作数据库的某种权利。
2. 审计。可以跟踪用户对数据库的操作。
3. 实现复杂的数据完整性规则
4. 实现复杂的非标准的数据库相关完整性规则。触发器可以对数据库中相关的表进行连环更新。
例如,在auths表author_code列上的删除触发器可导致相应删除在其它表中的与之匹配的行。
5. 同步实时地复制表中的数据。
6. 自动计算数据值,如果数据的值达到了一定的要求,则进行特定的处理。例如,如果公司的帐号上的资金低于5万元则立即给财务人员发送警告数据。
二、触发器语法
CREATE TRIGGER BEFORE|AFTER INSERT|UPDATE|DELETE ON # 表名 FOR EACH ROW # 这句话在mysql是固定的 BEGIN (调用NEW/OLD参数); END
例如下方代码
create trigger add_stuafter insert on student for each row begin insert into student_score ( stu_id, score, rank) values( NEW.stuid, NEW.username); -- NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据end;
MySQL 中定义了 NEW 和 OLD,用来表示触发器的所在表中,触发了触发器的那一行数据,来引用触发器中发生变化的记录内容,具体地:
① 在INSERT型触发器中,NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
② 在UPDATE型触发器中,OLD用来表示将要或已经被修改的原数据,NEW用来表示将要或已经修改为的新数据;
③ 在DELETE型触发器中,OLD用来表示将要或已经被删除的原数据;
另外,原则上请编写简单高效的触发执行语句,以免悄无声息的浪费过多资源你还不知道!
想我初三时常年倒数,成绩稳定,因此我拿当时几位老友排名数据来纪念一波(手动挠头)。给大家提供一个测试数据;(沿用的前两篇“视图”、“存储过程”博文中的数据)
a.学生表
CREATE TABLE `student` ( `ID` int NOT NULL AUTO_INCREMENT , `NAME` varchar(30) NOT NULL , `SEX` char(2) NOT NULL , `AGE` int NOT NULL , `CLASS` varchar(10) NOT NULL , `GRADE` varchar(20) NOT NULL , `HOBBY` varchar(100) NULL , PRIMARY KEY (`ID`))
#插入数据:
INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('1', '陈哈哈', '男', '15', '18班', '9年级', '上网');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('2', '扈亚鹏', '男', '15', '18班', '9年级', '美食');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('3', '刘晓莉', '女', '14', '18班', '9年级', '金希澈');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('4', '朱志鹏', '男', '15', '18班', '9年级', '睡觉');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('5', '徐立楠', '女', '14', '18班', '9年级', '阅读');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('6', '顾昊', '男', '15', '5班', '9年级', '篮球');INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('7', '陈子凝', '女', '15', '18班', '9年级', '看电影');
#插入结果:
b.成绩表
CREATE TABLE `student_score` ( `SID` int(11) NOT NULL, `S_NAME` varchar(30) NOT NULL, `TOTAL_SCORE` int(11) NOT NULL, `RANK` int(11) NOT NULL, PRIMARY KEY (`SID`))
#插入数据:
INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('1', '陈哈哈', '405', '1760');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('2', '扈亚鹏', '497', '1000');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('3', '刘晓莉', '488', '1170');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('4', '朱志鹏', '405', '1770');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('5', '徐立楠', '530', '701');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('6', '顾昊', '485', '1286');INSERT INTO `student_score` (`SID`, `S_NAME`, `TOTAL_SCORE`, `RANK`) VALUES ('7', '陈子凝', '704', '9');
#插入结果:
c.逃课上网表
CREATE TABLE `student_go_wangba` ( `SID` int(9) NOT NULL, `SGW_NAME` varchar(30) DEFAULT NULL, `TIMES` int(9) DEFAULT NULL, PRIMARY KEY (`SID`))
#插入数据:
INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('1', '陈哈哈', 15);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('2', '扈亚鹏', 1);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('3', '刘晓莉', 0);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('4', '朱志鹏', 63);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('5', '徐立楠', 0);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('6', '顾昊', 7);INSERT INTO `student_go_wangba` (`SID`, `SGW_NAME`, `TIMES`) VALUES ('7', '陈子凝', 0);
#插入结果:
表数据:以上面的三张表为例;学生表(student)、学生成绩表(student_score)、逃课上网次数表(student_go_wangba),均已学号(stuid)为主键。
需求:
那么,如何设计触发器A呢?
触发器A:
-- 新增触发器A,当student表插入数据时,student_score表生成初始关联数据DROP TRIGGER IF EXISTS add_stu;create trigger add_stuafter insert on student for each row begin INSERT INTO student_score (SID, S_NAME, TOTAL_SCORE, RANK) VALUES (new.ID,new.NAME, 0, 0 );end;
触发器B:
-- 新增触发器B,当student_score表插入数据时,student_go_wangba表生成初始关联数据DROP TRIGGER IF EXISTS add_score;create trigger add_scoreafter insert on student_score for each row begin INSERT INTO student_go_wangba (SID, SGW_NAME, TIMES) VALUES (new.SID,new.S_NAME, 0 );end;
查询一下我的触发器:
show triggers G -- "G"是干什么用的? -- 作用:在shell中树形展示
如果在Navicat中就不用 G,直接"show triggers;"就可以。
show triggers;
执行触发器,向student表中加入一条数据:
INSERT INTO `student` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `GRADE`, `HOBBY`) VALUES ('8', '李昂', '男', '15', '18班', '9年级', '看片儿');
结果如下图所示:
同时插入三个数据,两个触发器正确执行了~
注意:创建触发器和表一样,建议增加判断:DROP TRIGGER IF EXISTS `add_stu`;
表数据:以上面的三张表为例;学生表(student)、学生成绩表(student_score)、逃课上网次数表(student_go_wangba),均已学号(stuid)为主键。
需求:有些老是逃课上网的学生被开除,需要删掉所有信息,以免给学校抹黑~~~
那么,如何设计触发器C呢?
触发器C:
-- 新增触发器C,当student表删除数据时,student_score表删除关联数据DROP TRIGGER IF EXISTS del_stu;create trigger del_stuafter delete on student for each row begin DELETE FROM student_score where SID = old.ID;end;
触发器D:
-- 新增触发器D,当student_score表删除数据时,student_go_wangba表删除关联数据DROP TRIGGER IF EXISTS del_score;create trigger del_scoreafter delete on student_score for each row begin DELETE FROM student_go_wangba where SID = old.SID;end;
查询一下我的触发器(show triggers G):
执行触发器,从student表中删除一条数据:
DELETE FROM `student` where NAME = '朱志鹏'
结果如下图所示:
同时删除三个数据,触发器正确执行了。朱志鹏同学数据已经木得了~注意:创建触发器和表一样,建议增加判断:DROP TRIGGER IF EXISTS `del_stu`;
跟Insert触发器、Delete触发器同理,这里不再赘述
下面通过参考知乎、CSDN论坛、天涯社区,简单讲解几个内容供大家参考:
问题一: 触发器有哪些限制和注意事项
回答:
触发器会有以下两种限制:
注意事项:
MySQL的触发器是按照BEFORE触发器、行操作、AFTER触发器的顺序执行的,其中任何一步发生错误都不会继续执行剩下的操作,如果对事务表进行的操作,如果出现错误,那么将会被回滚,如果是对非事务表进行操作,那么就无法回滚了,数据可能会出错。
问题二: 大型系统必须得要存储过程和触发器吗?
回答1:
我们先要弄清楚二个问题:
接下来给你举一些例子:
一般情况下,会使用存储过程和触发器,减少开发成本,毕竟其业务逻辑修改频繁,而且为通用,很多时候会把一些业务逻辑编写成存储过程,像Oracle会写成包,比存储过程更强大。
另外一个原因是服务器的负载是可控,也即系统的访问人数首先是可控的,没有那么大,而且这些数据又非常关键,为此往往使用的设备也比较好,多用存储柜子支撑数据库。
比如淘宝、知乎、微博等,数据库的压力是非常大的,也往往会最容易成为瓶颈,而且多用PC服务器支撑,用户量的增速是不可控的,同时在线访问的用户量也是不可控的,为此肯定会把业务逻辑放到其他语言的代码层,而且可以借助一些LVS等类型软硬件做负载均衡,以及平滑增减Web层的服务器,从而达到线性的增减而支持大规模的访问。
所以不管你的这个系统是否庞大,首先要分业务支持的对象,系统最可能容易出现瓶颈的地方在那?
当然也不是说互联网行业的应用就绝对不用存储过程,这个也不对,曾在阿里做的Oracle迁移MySQL系统确实用了,因为历史的原因,另外还有一些新系统也有用,比如晚上进行定期的数据统计的一些操作,不过有量上的控制。存储过程是好东西,要分场景,分业务类型来用就可以把握好。
回答2:
回答3:
问题三: 为什么大家都不推荐使用MySQL触发器而用存储过程?
回答1:
回答2:
这种东西只有在并发不高的项目,管理系统中用。如果是面向用户的高并发应用,都不要使用。
触发器和存储过程本身难以开发和维护,不能高效移植。
回答3:
我觉得来自两方面的因素:
我认为性能上其实还是触发器占优势的,但是基于以上原因不受青睐。
目前在职Java开发,如果你现在也在学习Java,在入门学习Java的过程当中缺乏基础入门的视频教程, 可以关注并私信我:01。免费领取2020年最新Java基础精讲视频教程,学习手册,面试题,开发工具,PDF文档书籍教程,以下资料截图:
关注并私信我:01。即可领取以上学习资料。