触发器(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,用来表示触发器的所在表中,触发了触发器的那一行数据,来引用触发器中发生变化的记录内容,具体地:
- 在INSERT型触发器中,NEW用来表示将要(BEFORE)或已经(AFTER)插入的新数据;
- 在UPDATE型触发器中,OLD用来表示将要或已经被修改的原数据,NEW用来表示将要或已经修改为的新数据;
- 在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);
结果如图:

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

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);