数据库学习:触发器


触发器(Trigger)

触发器 (Trigger) 是一个存储过程(stored Procedure),在适当条件下被调用的过程,有点类似于回调函数与生命周期函数(在某个时刻会自动执行的函数)。触发器是mysql5新增的功能,通常在进行增删改操作后会做一些触发操作。

创建触发器的语法

CREATE
    [DEFINER = { user | CURRENT_USER }]
TRIGGER trigger_name
trigger_time trigger_event
ON tbl_name FOR EACH ROW
  [trigger_order]
trigger_body

trigger_time: { BEFORE | AFTER }

trigger_event: { INSERT | UPDATE | DELETE }

trigger_order: { FOLLOWS | PRECEDES } other_trigger_name
  • BEFORE 和 AFTER 参数指定了触发执行的时间,在事件之前或是之后。
  • FOR EACH ROW 表示任何一条记录上的操作满足触发事件都会触发该触发器,也就是说触发器的触发频率是针对每一行数据触发一次。

tigger_event详解:

  ①INSERT型触发器:插入某一行时激活触发器,可能通过INSERT、LOAD DATA、REPLACE 语句触发(LOAD DAT语句用于将一个文件装入到一个数据表中,相当与一系列的INSERT操作);

  ②UPDATE型触发器:更改某一行时激活触发器,可能通过UPDATE语句触发;

  ③DELETE型触发器:删除某一行时激活触发器,可能通过DELETE、REPLACE语句触发。

trigger_order是MySQL5.7之后的一个功能,用于定义多个触发器,使用follows(尾随)或precedes(在…之先)来选择触发器执行的先后顺序。

1、创建只有一个执行语句的触发器

CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件 ON 表名 FOR EACH ROW 执行语句;

例1:创建了一个名为trig1的触发器,一旦在work表中有插入动作,就会自动往time表里插入当前时间

mysql> CREATE TRIGGER trig1 AFTER INSERT
    -> ON `work` FOR EACH ROW
    -> INSERT INTO time VALUES(NOW());

2、创建有多个执行语句的触发器

CREATE TRIGGER 触发器名 BEFORE|AFTER 触发事件

ON 表名 FOR EACH ROW

BEGIN

​ 执行语句列表

END;

例2:定义一个触发器,一旦有满足条件的删除操作,就会执行BEGIN和END中的语句

mysql> DELIMITER ||
mysql> CREATE TRIGGER trig2 BEFORE DELETE
    -> ON `work` FOR EACH ROW
    -> BEGIN
    ->   INSERT INTO time VALUES(NOW());
    ->   INSERT INTO time VALUES(NOW());
    -> END||
mysql> DELIMITER ;

3、NEW与OLD详解

MySQL 中定义了 NEW 和 OLD,用来表示触发器的所在表中,触发了触发器的那一行数据,来引用触发器中发生变化的记录内容,具体地:

  1. 在INSERT型触发器中,NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
  2. 在UPDATE型触发器中,OLD用来表示将要或已经被修改的原数据,NEW用来表示将要或已经修改为的新数据;
  3. 在DELETE型触发器中,OLD用来表示将要或已经被删除的原数据;

使用方法:

NEW.columnName (columnName为相应数据表某一列名)

另外,OLD是只读的,而NEW则可以在触发器中使用 SET 赋值,这样不会再次触发触发器,造成循环调用(如每插入一个学生前,都在其学号前加“2013”)。

mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);

mysql> delimiter $$
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
    -> FOR EACH ROW
    -> BEGIN
    ->   IF NEW.amount < 0 THEN
    ->     SET NEW.amount = 0;
    ->   ELSEIF NEW.amount > 100 THEN
    ->     SET NEW.amount = 100;
    ->   END IF;
    -> END$$
mysql> delimiter ;

mysql> update account set amount=-10 where acct_num=137;
mysql> select * from account;
+----------+---------+
| acct_num | amount  |
+----------+---------+
|      137 |    0.00 |
|      141 | 1937.50 |
|       97 | -100.00 |
+----------+---------+

mysql> update account set amount=200 where acct_num=137;

mysql> select * from account;
+----------+---------+
| acct_num | amount  |
+----------+---------+
|      137 |  100.00 |
|      141 | 1937.50 |
|       97 | -100.00 |
+----------+---------+

二、查看触发器

1、SHOW TRIGGERS 语句查看触发器信息

mysql> SHOW TRIGGERS\G;

……

结果,显示所有触发器的基本信息;无法查询指定的触发器。

2、在information_schema.triggers表中查看触发器信息

mysql> SELECT * FROM information_schema.triggers\G

……

结果,显示所有触发器的详细信息;同时,该方法可以查询制定触发器的详细信息。

mysql> select * from information_schema.triggers 
    -> where trigger_name='upd_check'\G;

Tips:

  所有触发器信息都存储在information_schema数据库下的triggers表中,可以使用SELECT语句查询,如果触发器信息过多,最好通过TRIGGER_NAME字段指定查询。

三、删除触发器

DROP TRIGGER [IF EXISTS] [schema_name.]trigger_name

删除触发器之后最好使用上面的方法查看一遍;同时,也可以使用database.trig来指定某个数据库中的触发器。

下面由一个实例来完成今天的学习。

例子:股票涨跌幅

我们以股票为例,股票由日期、当天尾盘价格、成交量组成,首先创一个table。

# 1.例子:股票收盘价,计算每天相对的涨跌
# 计算规则:(今天的收盘价-前一天的收盘价)/前一天的收盘价 × 100% = ((今天收盘价/前一天收盘价) - 1 ) × 100%
USE WORLD;
DROP TABLE IF EXISTS STOCK_DAILY;
CREATE TABLE STOCK_DAILY(
    DATE DATE,
    CLOSE_PRICE DECIMAL(10,2),
    AMOUNT DECIMAL(10,2),
    PRIMARY KEY(DATE)
);

-- 把生成的INCREASINGRATE写入表中
ALTER TABLE STOCK_DAILY ADD INCREASINGRATE DECIMAL(10,2);

利用Trigger使得数据被insert时自动计算涨跌幅

# 1. INSERT 时自动计算并填写INCREASINGRATE
DROP TRIGGER IF EXISTS UPDATE_INCREASINGRATE_TGR;
# 创建一个触发器
CREATE TRIGGER UPDATE_INCREASINGRATE_TGR
BEFORE INSERT ON STOCK_DAILY # 在对表进行插入操作时
FOR EACH ROW
BEGIN
    DECLARE PRE_CLOSE_PRICE DECIMAL(10,2) DEFAULT NULL;
    # 拿前一天的收盘价作为pre_close_price
    SELECT CLOSE_PRICE FROM stock_daily WHERE DATE = NEW.DATE - 1 INTO PRE_CLOSE_PRICE; 
    # 避免排他锁不用insert
    SET NEW.INCREASINGRATE = (NEW.CLOSE_PRICE/PRE_CLOSE_PRICE - 1) * 100.0;
    
END;

INSERT INTO STOCK_DAILY
VALUES
('2020-01-01',25.03,500000),
('2020-01-02',21.02,500000),
('2020-01-03',24.17,500000),
('2020-01-04',22.84,500000),
('2020-01-05',26.77,500000),
('2020-01-06',28.52,500000),
('2020-01-07',32.51,500000);

结果如图:

image-20220629234004915

要注意的是,在增删改的时候,当前的数据记录,不可以在被Triger增删改

image-20220629234650170

Trigger如何取消正在运行的增删改操作

DROP TRIGGER IF EXISTS UPDATE_INCREASINGRATE_TGR;
# 创建一个触发器
CREATE TRIGGER UPDATE_INCREASINGRATE_TGR
AFTER INSERT ON STOCK_DAILY # 在对表进行插入操作时
FOR EACH ROW
BEGIN
        IF NEW.INCREASINGRATE IS NULL THEN
            SIGNAL SQLSTATE '12345' SET MESSAGE_TEXT='IncreasingRate is Null, Message from mysql.';
        END IF;
END;

INSERT INTO stock_daily (DATE,CLOSE_PRICE,AMOUNT)
VALUES
('2020-01-08',37.51,500000);

参考博文

https://www.cnblogs.com/geaozhang/p/6819648.html


文章作者: silverCorriods
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 silverCorriods !
评论
  目录