[置顶] 《MySQL必知必会学习笔记》:触发器

《MySQL必知必会学习笔记》:触发器

什么是触发器???

简单来说,就是监视某个事件A,触发某个动作(或事件)B。

例如:当我们的订单中卖掉100个apple,则我们的商品表中的apple数量就要自动减少100.

触发器是MySQL响应insert、update、delete这3个语句而自动执行的一条MySQL语句(或位于begin end之间的一组语句)。

创建触发器

创建触发器有4个要素:监视事件(insert/delete/update)、监视地点(table)、触发事件(一些操作)、触发时间(Before/after).

DELIMITER $$
	CREATE TRIGGER `test`.`tg_insert` AFTER INSERT -- after 为触发时间 -- insert 为监视事件 ON `test`.`student` -- student 为 监视地点 FOR EACH ROW BEGIN -- begin end中间的就是 触发事件,即要做的事情 -- select 'insert date in student table' ;这句话报错:Not allowed to return a result set from a trigger SELECT 'insert data in student table' INTO @res;
    END$$ DELIMITER ;

上面这个例子的触发器的四要素如下

监视事件为:insert

监视地点:表student

触发事件:SELECT ‘insert data in student table’ INTO @res;//将此字符串保存到变量@res中

触发时间:after。

删除触发器

触发器不能更新,只能删除,删除命令如下:

drop trigger tg_name;

触发器例子讲解

触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器(每条insert、update和delete的before和after);单一触发器不能与多个事件或多个表关联。

下面就以买卖火车票进行讲解。我们都知道,当我们下一个订单之后就表示我们将买一张(或多张)某地点的火车票,因此剩余火车票表中相应的数据就要进行一定的更新处理。否则就会出现一直看着有票但是一直买不到的情况(虽然现实生活中买火车票时就是这样,看着看着有很多票,一下子就没了)。

现在我们创建2个表,一个表为订单,一个表为火车票剩余票的表。

创建表的命令如下:

订单表如下:

create table ordersintrain( id int(10) primary key auto_increment, trainID int(10) not null, buyTicketNum int(10) not null);

火车票库存表如下:

create table ticket( trainID int(10) not null, remainderNum int(10) not null);

创建此表之后,我们为ticket添加了如下的内容:

即由3趟火车,每趟火车现在的余票为100张。

现在我们创建一个触发器,触发器的作用就是:当有人买票后,ticket的余票数要发生相应的变化。

DELIMITER $$

CREATE TRIGGER `test`.`tg_ticket` AFTER INSERT ON `test`.`ordersintrain` FOR EACH ROW BEGIN UPDATE ticket SET remainderNum=remainderNum-new.buyTicketNum WHERE trainID=new.trainID;  -- new.trainID 指的是ordersintrain表中的trainID
    END$$ DELIMITER ;

有了触发器之后,我们为订单表ordersintrain插入一行内容,如下:

insert into ordersintrain(trainID,buyTicketNum) values(424,5);

当执行完这条语句之后,ticket表中的结果就发生了相应的变化,如下:

这就说明,我们的触发器起了作用。

现在假设出现了一些情况,这个订单的买票的内容要发生变化,在我们的生活中比较常见,变化可能有如下两种情况:

1、退几张票,不退完。

2、撤销订单。

面对这两种情况,应该怎么来写触发器呢??

先看第一种情况

触发器的四要素如下:

监视地点:ordersintrain

监视事件:update

触发时间:after

触发事件:更新ticket表

对于update,被修改的行,修改前的数据用old 表示,old.列名引用被修改前行的数据,修改后的数据用 new 表示,new.列名引用被修改后行的数据。

触发器的创建如下:

DELIMITER $$
CREATE TRIGGER `test`.`tg_ticket_update` AFTER UPDATE ON `test`.`ordersintrain` FOR EACH ROW BEGIN UPDATE ticket SET remainderNum=remainderNum+old.buyTicketNum-new.buyTicketNum WHERE trainID=old.trainID/new.trainID;  -- 先把旧的数据恢复,然后减去新的数据。
    END$$ DELIMITER ;

当执行如下的操作后:

UPDATE ordersInTrain SET buyTicketNum=3 WHERE trainID=424;

里面上执行上面的操作之后,会触发tg_ticket_update触发器,但是,实验表明:并没有触发该触发器。查找原因,原因如下:

触发事件不能这样写:

UPDATE ticket SET remainderNum=(remainderNum+old.buyTicketNum-new.buyTicketNum) WHERE trainID= old.trainID/ new.trainID ;(注意红色标注的这里) ;但是参考博客中这样写居然是Ok的,这就不能而知了,在实验过程中,是不能这样写的。

应该这样写,将一句代码分成两步来做:

    UPDATE ticket SET remainderNum=(remainderNum+old.buyTicketNum) WHERE trainID=old.trainID ; -- 先加入旧的数据
    UPDATE ticket SET remainderNum=(remainderNum-new.buyTicketNum) WHERE trainID=new.trainID ;  -- 再减去新的数据

这种改写之后,此触发器在执行下面这条语句之后就能够被触发了,触发后ticket表中trainID=424的票数为97 张了。

UPDATE ordersInTrain SET buyTicketNum=3 WHERE trainID=424;

第二种情况:撤销订单

这种情况的触发器的四要素如下。

监视地点:ordersintrain

监视事件:delete

触发时间:after

触发事件:更新ticket表

DELIMITER $$
CREATE TRIGGER `test`.`tg_ticket_delete` AFTER DELETE ON `test`.`ordersintrain` FOR EACH ROW BEGIN UPDATE ticket SET remainderNum=remainderNum+old.buyTicketNum WHERE trainID=old.trainID;
    END$$ DELIMITER ;

为方便观察结果,将上面进行的操作全部还原。即在初始状态下,每个trainID对应的车票为100,经过如下的插入语句:

INSERT INTO ordersInTrain(trainID,buyTicketNum) VALUES(424,5);

之后,触发了insert插入器,trainID=424的余票变为了95张,即少了5张。

接着执行如下的语句:

DELETE FROM ordersintrain WHERE trainID=424;

根据结果发现,trainID=424的余票变为了原来的100张。这就说明delete触发器起了作用。

before与after的区别

上面的例子中讲解了3中触发器,分别为: (insert/update/delete)+after.除了这3种,还有如下的3中触发器:
(insert/update/delete)+before .

下面主要介绍after和before关键字的区别。

after :是先完成数据的增删改,再触发,触发的语句晚于监视的增删改操作,无法影响前面的增删改;

before :是先完成触发,再完成监视事件的增删改,即触发的语句先于监视的增删改,因此,我们就有机会对监视事件的增删改进行检查,例如,检查插入的数据是否有效(例如:一张火车票的价格不能是1000000000元呀),格式是否正确等。

还是以上面的例子为例进行讲解。

现在,ticket表中的内容如下:

还是建立一个after insert 触发器

DELIMITER $$

CREATE TRIGGER `test`.`tg_ticket` after INSERT ON `test`.`ordersintrain` FOR EACH ROW BEGIN UPDATE ticket SET remainderNum=remainderNum-new.buyTicketNum WHERE trainID=new.trainID;  -- new.trainID 指的是ordersintrain表中的trainID
    END$$ DELIMITER ;

即在插入之后进行触发。当执行如下的语句时,会出现什么样的结果呢???

insert into ordersintrain(trainID,buyTicketNum) values(424,200); -- 一个买200张票的订单

从语句中可以看出,这是一个订单为200张票的订单,如果是 after insert触发器。则结果如下:

从结果可以看出,由于after insert对监视事件的插入数据没有进行有效性检查,因此出现的负数

如果创建一个before insert触发器,则可以在插入之前进行检查,避免这样的事情发生。

DELIMITER $$

CREATE TRIGGER `test`.`tg_ticket_before` BEFORE INSERT ON `test`.`ordersintrain` FOR EACH ROW BEGIN IF new.buyTicketNum>100 THEN SET new.buyTicketNum=100;
    END IF ;
    UPDATE ticket SET remainderNum=remainderNum-new.buyTicketNum WHERE trainID=new.trainID;
    END$$ DELIMITER ;

则订单插入只会插入100,且ticket 的余票也不会变为负数。

参考资料

1、《MySQL必知必会》

2、blog:http://www.cnblogs.com/zzwlovegfj/archive/2012/07/04/2576989.html

你可能感兴趣的:(触发器,mysql,update,trigger,insert)