8. MySQL -- 触发器

目录

触发器概念

语法

案例

创建表

修改库存触发器

触发器的改进

存在的两种问题

触发器before和after的区别

after和before的区别:

新建触发器:

为什么大家都不推荐使用MySQL触发器而用存储过程?


触发器概念

       触发器(trigger):监视某种情况,并触发某种操作,它是提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,例如当对一个表进行操作( insert,delete, update)时就会激活它执行。

       触发器经常用于加强数据的完整性约束和业务规则等。 触发器创建语法四要素:

  1. 监视地点(table)
  2. 监视事件(insert/update/delete)
  3. 触发时间(after/before)
  4. 触发事件(insert/update/delete)

语法

8. MySQL -- 触发器_第1张图片

  1. trigger_time是触发器的触发事件,可以为before(在检查约束前触发)或after(在检查约束后触发);
  2. trigger_event是触发器的触发事件,包括insert、update和delete,需注意对同一个表相同触发时间的相同触发事件,只能定义一个触发器;
  3. 可以使用old和new来引用触发器中发生变化的记录内容。
DROP TRIGGER IF EXISTS `triggerName`; #判断触发器是否存在,存在删除

create trigger triggerName

after/before insert/update/delete on 表名

for each row   #这句话在mysql是固定的

begin

    sql语句;

end;
 

 

案例

创建表

首先我们来创建两张表:

#商品表

create table g

(

  id int primary key auto_increment,

  name varchar(20),

  num int

);

#订单表

create table o

(

  oid int primary key auto_increment,

   gid int,

     much int

);
insert into g(name,num) values('商品1',10),('商品2',10),('商品3',10);

 

 

修改库存触发器

如果我们在没使用触发器之前:假设我们现在卖了3个商品1,我们需要做两件事

1.往订单表插入一条记录

insert into o(gid,much) values(1,3);

2.更新商品表商品1的剩余数量

update g set num=num-3 where id=1;

现在,我们来创建一个触发器,需要先执行该语句:delimiter $(意思是告诉mysql语句的结尾换成以$结束)

create trigger tg1 after insert on o for each row

begin

    update g set num=num-3 where id=1;

end$

这时候我们只要执行:

insert into o(gid,much) values(1,3)$

会发现商品1的数量变为7了,说明在我们插入一条订单的时候,触发器自动帮我们做了更新操作。

触发器的改进

       但现在会有一个问题,因为我们触发器里面num和id都是写死的,所以不管我们买哪个商品,最终更新的都是商品1的数量。比如:我们往订单表再插入一条记录:insert into o(gid,much) values(2,3),执行完后会发现商品1的数量变4了,而商品2的数量没变,这样显然不是我们想要的结果。我们需要改改我们之前创建的触发器。

我们如何在触发器引用行的值,也就是说我们要得到我们新插入的订单记录中的gid或much的值。

       对于insert而言,新插入的行用new来表示,行中的每一列的值用new.列名来表示。所以现在我们可以这样来改我们的触发器

create trigger tg2 after insert on o for each row

begin

    update g set num=num-new.much where id=new.gid;(注意此处和第一个触发器的不同)

end$

第二个触发器创建完毕,我们先把第一个触发器删掉

drop trigger tg1$

再来测试一下,插入一条订单记录:

insert into o(gid,much) values(2,3)$

执行完发现商品2的数量变为7了,现在就对了。

存在的两种问题

现在还存在两种情况:

  1. 当用户撤销一个订单的时候,我们这边直接删除一个订单,我们是不是需要把对应的商品数量再加回去呢?
  2. 当用户修改一个订单的数量时,我们触发器修改怎么写?
  • 第一种情况:

监视地点:o表

监视事件:delete

触发时间:after

触发事件:update

       对于delete而言:原本有一行,后来被删除,想引用被删除的这一行,old来表示,old.列名可以引用被删除的行的值。那我们的触发器就该这样写:

create trigger tg3 after delete on o for each row

begin

    update g set num = num + old.much where id = old.gid;(注意这边的变化)

end$

创建完毕。

再执行

delete from o where oid = 2$

会发现商品2的数量又变为10了。


  • 第二种情况:

监视地点:o表

监视事件:update

触发时间:after

触发事件:update

对于update而言:

       被修改的行,修改前的数据,用old来表示,old.列名引用被修改之前行中的值修改的后的数据,用new来表示,new.列名引用被修改之后行中的值

那我们的触发器就该这样写:

create trigger tg4 after update on o for each row

begin

    update g set num = num+old.much-new.much where id = old/new.gid;

end$

先把旧的数量恢复再减去新的数量就是修改后的数量了。

我们来测试下:先把商品表和订单表的数据都清掉,易于测试。

假设我们往商品表插入三个商品,数量都是10,

买3个商品1:

insert into o(gid,much) values(1,3)$ 

这时候商品1的数量变为7;

我们再修改插入的订单记录:

update o set much = 5 where oid = 1$

我们变为买5个商品1,这时候再查询商品表就会发现商品1的数量只剩5了,说明我们的触发器发挥作用了。

触发器before和after的区别

after和before的区别:

  1. after是先完成数据的增删改,再触发,触发的语句晚于监视的增删改操作,无法影响前面的增删改动作;也就是说先插入订单记录,再更新商品的数量;
  2. before是先完成触发,再增删改,触发的语句先于监视的增删改,我们就有机会判断,修改即将发生的操作;

假设:假设商品表有商品1,数量是10;

我们往订单表插入一条记录:

insert into o(gid,much) values(1,20);

       会发现商品1的数量变为-10了。这就是问题的所在,因为我们之前创建的触发器是after,也就是说触发的语句是在插入订单记录之后才执行的,这样我们就无法判断新插入订单的购买数量

新建触发器:

#监视地点: 商品表o

#监视事件:insert

#触发时间:before

#触发事件:update

案例:当新增一条订单记录时,判断订单的商品数量,如果数量大于10,就默认改为10

create trigger tg6 before insert on o for each row

begin

  if new.much > 10 then

    set new.much = 10;

  end if;

  update g set num = num - new.much where id = new.gid;

end$

执行完,把之前创建的after触发器删掉,再来插入一条订单记录:

insert into o(gid,much) valus(1,20)$

执行完会发现订单记录的数量变为10,商品1的数量变为0了,就不会出现负数了。

为什么大家都不推荐使用MySQL触发器而用存储过程?

  1. 存储过程和触发器二者是有很大的联系的,我的一般理解就是触发器是一个隐藏的存储过程,因为它不需要参数,不需要显示调用,往往在你不知情的情况下已经做了很多操作。从这个角度来说,由于是隐藏的,无形中增加了系统的复杂性,非DBA人员理解起来数据库就会有困难,因为它不执行根本感觉不到它的存在。
  2. 再有,涉及到复杂的逻辑的时候,触发器的嵌套是避免不了的,如果再涉及几个存储过程,再加上事务等等,很容易出现死锁现象,再调试的时候也会经常性的从一个触发器转到另外一个,级联关系的不断追溯,很容易使人头大。其实,从性能上,触发器并没有提升多少性能,只是从代码上来说,可能在coding的时候很容易实现业务,所以我的观点是:摒弃触发器!触发器的功能基本都可以用存储过程来实现。
  3. 在编码中存储过程显示调用很容易阅读代码,触发器隐式调用容易被忽略。
  4. 存储过程的致命伤在于移植性,存储过程不能跨库移植,比如事先是在mysql数据库的存储过程,考虑性能要移植到oracle上面那么所有的存储过程都需要被重写一遍。
  5. 存储过程需要显式调用,意思是阅读源码的时候你能知道存储过程的存在,而触发器必须在数据库端才能看到,容易被忽略。
  6. Mysql的触发器本身不是很好,比如after delete无法链式反应的问题

我认为性能上其实还是触发器占优势的,但是基于以上原因不受青睐。

你可能感兴趣的:(#,MySql,触发器,监视地点,监视事件,触发时间,触发事件)