zoukankan      html  css  js  c++  java
  • 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 的余票也不会变为负数。

    转自:http://blog.csdn.net/u010412719/article/details/51136077

  • 相关阅读:
    SQL Server 2005高级程序设计
    SQL语言艺术
    无益的程序
    Django Ajax动态图形监控
    C/C++ Qt 基本文件读写方法
    Django 实现统计网站访问状态
    Python 实现 WebSocket 通信
    Django Admin后台定制简单监控页
    Django Ajax 实现Web命令行执行
    C/C++ Qt QThread 线程组件应用
  • 原文地址:https://www.cnblogs.com/lmaster/p/6373375.html
Copyright © 2011-2022 走看看