MySQL从5.0.2版本开始支持触发器。MySQL的触发器和存储过程一样,都是嵌入到MySQL服务器的一段程序。
触发器是由事件来触发某个操作,事件就是指用户的动作或者触发某项行为,这些事件包括INSERT、UPDATE、DELETE事件。如果定义了触发程序,当数据库执行这些语句时候,就相当于事件发生了,就会自动激发触发器执行相应的操作。
当对数据表中的数据执行插入、更新和删除操作,需要自动执行一些数据库逻辑时,可以使用触发器来实现。
创建触发器需要给出以下4条信息:
- 唯一的触发器名(最好是在数据库范围内唯一);
- 触发器关联的表;
- 触发器应该响应的活动(DELETE、INSERT或UPDATE);
- 触发器何时执行(处理之前或之后)。
只有表才支持触发器,视图和临时表不支持。触发器按每个表每个事件每次地定义,每个表每个事件每次只允许一个触发器。因此,每个表最多支持6个触发器(每条INSERT、UPDATE 和DELETE的之前和之后)。单一触发器不能与多个事件或多个表关联,所以如果需要一个对INSERT和UPDATE操作执行的触发器,则应该定义两个触发器。
如果BEFORE触发器失败,则MySQL将不执行请求的操作。如果BEFORE触发器或语句本身失败,MySQL将不执行AFTER触发器。
建立触发器
CREATE TRIGGER 触发器名称
{BEFORE|AFTER} {INSERT|UPDATE|DELETE} ON 表名
FOR EACH ROW
触发器执行的语句块;
- 表名:表示触发器监控的对象。
BEFORE|AFTER
:表示触发的时间。BEFORE 表示在事件之前触发;AFTER 表示在事件之后触发。INSERT|UPDATE|DELETE
:表示触发的事件。INSERT
表示插入记录时触发;UPDATE
表示更新记录时触发;DELETE
表示删除记录时触发。
触发器执行的语句块:可以是单条SQL语句,也可以是由BEGIN…END结构组成的复合语句块。
INSERT触发器
INSERT
触发器在INSERT
语句执行之前或之后执行在
INSERT
触发器代码内,可引用一个名为**NEW**
的虚拟表,访问被插入的行;- 在
BEFORE INSERT
触发器中,**NEW**
中的值也可以被更新(允许更改被插入的值); - 对于
AUTO_INCREMENT
列,**NEW**
在INSERT执行之前包含0,在INSERT执行之后包含新的自动生成值。 ```sql CREATE TRIGGER neworder AFTER INSERT ON orders FOR EACH ROW SELECT NEW.order_num INTO @new_order_num;
INSERT INTO orders(order_date,cust_id) VALUES(Now(),10001);
SELECT @new_order_num;
<a name="FgVfP"></a>
## DELETE触发器
`DELETE`触发器在`DELETE`语句执行之前或之后执行
- 在`DELETE`触发器代码内,可以引用一个名为`OLD`的虚拟表,访问被删除的行;
- `OLD`中的值全都是只读的,不能更新。
```sql
delimiter //
CREATE TRIGGER deleter_order BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
INSERT INTO archive_orders(order_num,order_rate,cust_id)
VALUES(OLD.order_num, OLD.order_date, OLD.cust_id);
END //
delimiter ;
UPDATE触发器
UPDATE
触发器:UPDATE
触发器在UPDATE
语句执行之前或之后执行
- 在
UPDATE
触发器代码中,可以引用一个名为OLD
的虚拟表访问以前(UPDATE
语句前)的值,引用一个名为NEW
的虚拟表访问新 更新的值; - 在
BEFORE UPDATE
触发器中,NEW
中的值可能也被更新(允许更改将要用于UPDATE
语句中的值); OLD
中的值全都是只读的,不能更新。CREATE TRIGGER unpdate_vendor BEFORE UPDATE ON vendors
FOR EACH ROW SET NEW.vend_state = UPPER(NEW.vend_state);
示例
```sql — 创建数据表 CREATE TABLE test_trigger ( id INT PRIMARY KEY AUTO_INCREMENT, t_note VARCHAR(30) );
CREATE TABLE test_trigger_log ( id INT PRIMARY KEY AUTO_INCREMENT, t_log VARCHAR(30) ); — 创建触发器: 创建名称为before_insert的触发器,向test_trigger数据表插入数据之前, — 向test_trigger_log数据表中插入before_insert的日志信息。 DELIMITER //
CREATE TRIGGER before_insert BEFORE INSERT ON test_trigger FOR EACH ROW BEGIN INSERT INTO test_trigger_log (t_log) VALUES(‘before_insert’); END //
DELIMITER ;
— 向test_trigger数据表中插入数据 SELECT * FROM test_trigger_log;
— 创建名称为after_insert的触发器,向test_trigger数据表插入数据之后, — 向test_trigger_log数据表中插入after_insert的日志信息。 DELIMITER //
CREATE TRIGGER after_insert AFTER INSERT ON test_trigger FOR EACH ROW BEGIN INSERT INTO test_trigger_log (t_log) VALUES(‘after_insert’); END //
DELIMITER ;
— 向test_trigger数据表中插入数据 INSERT INTO test_trigger (t_note) VALUES (‘测试 AFTER INSERT 触发器’);
— 查看test_trigger_log数据表中的数据 SELECT * FROM test_trigger_log;
<a name="lECep"></a>
# 查看、删除触发器
查看触发器是查看数据库中已经存在的触发器的定义、状态和语法信息等。
```sql
-- 查看当前数据库的所有触发器的定义
SHOW TRIGGERS;
-- 查看当前数据库中某个触发器的定义
SHOW CREATE TRIGGER 触发器名;
-- 从系统库information_schema的TRIGGERS表中查询触发器的信息。
SELECT * FROM information_schema.TRIGGERS;
触发器不能更新或覆盖。为了修改一个触发器,必须先删除它, 然后再重新创建。
-- 删除触发器
DROP TRIGGER IF EXISTS 触发器名称;
触发器的优缺点
优点:
- 触发器可以确保数据的完整性;
- 触发器可以帮助记录操作日志,这有利于还原操作执行的具体场景,更好地定位问题。
- 触发器还可以用在操作数据前,对数据进行合法性检查;
缺点:
- 因为触发器存储在数据库中,并且由事件驱动,这就意味着触发器有可能不受应用层的控制。这对系统维护是非常有挑战的。
- 相关数据变更,特别是数据表结构的变更,都可能会导致触发器出错进而影响数据操作的正常运行。这些都会由于触发器本身的隐蔽性,影响到应用中错误原因排查的效率。
注意:
如果在子表中定义了外键约束,并且外键指定了ON UPDATE/DELETE CASCADE/SET NULL子句,此时修改父表被引用的键值或删除父表被引用的记录行时,也会引起子表的修改和删除操作,此时基于子表的UPDATE和DELETE语句定义的触发器并不会被激活。