时间:2021-07-01 10:21:17 帮助过:12人阅读
2. 触发器的理解
3. 触发器的例子
3.1. 触发器的使用的例子
3.2. 触发器和事务的例子
3.3. 在触发器中修改所在行的数据
4. 触发器注意事项
4.1. 触发器不能调用返回结果集的存储过程
4.2. 触发器中不能导致循环触发的语句
4.3. 触发器的效率问题
触发器的语法如下:
CREATE TRIGGER trigger_name trigger_time trigger_event
ON tbl_name FOR EACH ROW trigger_stmt
其中,trigger_time的值为BEFORE或者AFTER,表示触发器的语句在激活该触发器的语句之前或之后执行。
trigger_event指定激活该触发器的语句的类型,包括以下几种:
INSERT:将新行插入到数据表中,包括INSERT、LOAD DATA 和REPLACE语句。
UPDATE:修改现存行的数据,包括UPDATE语句。
DELETE:删除现存数据,包括DELETE和REPLACE语句。
对于同一个数据表的同一个事件和同一个时间,不能有两个触发器。比如,对于数据表table1,不可以有两个BEFORE INSERT触发器,也不能有两各AFTER INSERT触发器。但是允许有一个BEOFRE INSERT触发器和一个AFTER INSERT触发器同时存在。
在触发器中,使用别名OLD和NEW分别引用动作发生时的原有记录数据和新记录数据。
事件 |
OLD(只读) |
NEW(可使用SET修改) |
BEFORE INSERT |
无效 |
将要插入的数据 |
AFTER INSERT |
无效 |
已经插入的数据 |
BEFORE UPDATE |
将要被修改的原始数据 |
将要代替原始数据的新数据 |
AFTER UPDATE |
已经被修改的原始数据 |
已经代替了原始数据的新数据 |
BEFORE DELETE |
将要被删除的原始数据 |
无效 |
AFTER DELETE |
已经被删除的原始数据 |
无效 |
如果在触发器中使用了无效的OLD或NEW,则在创建触发器时会报错。
(1)在INSERT触发器中不允许使用OLD。
mysql> delimiter $$
mysql> create trigger tr_p0 before insert
-> on test1 for each row
-> begin
-> insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
-> insert into t1 values ( concat(OLD.value, NEW.value) );
-> end;
-> $$
ERROR 1363 (HY000): There is no OLD row in on INSERT trigger
mysql> delimiter ;
(2)在DELETE触发器中不允许使用NEW。
mysql> delimiter $$
mysql> create trigger tr_p2 before delete
-> on test1 for each row
-> begin
-> insert into history (old_value,new_value,time) values (OLD.value, NEW.value, current_timestamp());
-> end;
-> $$
ERROR 1363 (HY000): There is no NEW row in on DELETE trigger
mysql> delimiter ;
创建触发器时,ON指定的数据表tbl_name必须已经存在;不允许创建针对不存在的数据表的触发器。
TRUNCATE TABLE语句并不会触发基于DELETE事件的触发器。
假定存在以下数据表:
mysql> create table history (id integer auto_increment primary key, old_value varchar(100), new_value varchar(100), time timestamp);
Query OK, 0 rows affected (0.02 sec)
mysql> create table test1 ( value varchar(100) primary key);
Query OK, 0 rows affected (0.03 sec)
需求:
使用history表来记录test1表的记录的更新历史情况。
解决办法:
创建三个触发器:
(1)触发器1:在test1表新增记录时,向history数据表插入一条记录,记录初始值。
(2)触发器2:在test1表修改记录时,向history数据表插入一条记录,记录旧值和新值。
(3)触发器3:在test1表删除记录时,向hisotry数据表插入一条记录,记录最后的值。
触发器1:
drop trigger if exists tr_p0;
delimiter $$
create trigger tr_p0 before insert
on test1 for each row
begin
insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
end;
$$
delimiter ;
触发器2:
drop trigger if exists tr_p1;
delimiter $$
create trigger tr_p1 before update
on test1 for each row
begin
insert into history (old_value,new_value,time) values (OLD.value, NEW.value, current_timestamp());
end;
$$
delimiter ;
触发器3:
drop trigger if exists tr_p2;
delimiter $$
create trigger tr_p2 before delete
on test1 for each row
begin
insert into history (old_value,new_value,time) values (OLD.value, NULL, current_timestamp());
end;
$$
delimiter ;
测试结果:
mysql> insert into test1 values (‘001‘);
Query OK, 1 row affected (0.01 sec)
mysql> select * from test1;
+-------+
| value |
+-------+
| 001 |
+-------+
1 row in set (0.00 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
+----+-----------+-----------+---------------------+
1 row in set (0.00 sec)
mysql> insert into test1 values (‘002‘);
Query OK, 1 row affected (0.01 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
+----+-----------+-----------+---------------------+
2 rows in set (0.00 sec)
mysql> update test1 set value=‘A001‘ where value=‘001‘;
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
+----+-----------+-----------+---------------------+
3 rows in set (0.00 sec)
mysql> select * from test1;
+-------+
| value |
+-------+
| 002 |
| A001 |
+-------+
2 rows in set (0.00 sec)
mysql> update test1 set value=‘A001B‘ where value=‘A001‘;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test1;
+-------+
| value |
+-------+
| 002 |
| A001B |
+-------+
2 rows in set (0.00 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
+----+-----------+-----------+---------------------+
4 rows in set (0.00 sec)
mysql> delete from test1 where value=‘A001B‘;
Query OK, 1 row affected (0.02 sec)
mysql> select * from test1;
+-------+
| value |
+-------+
| 002 |
+-------+
1 row in set (0.00 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
| 31 | A001B | NULL | 2017-08-29 11:51:20 |
+----+-----------+-----------+---------------------+
5 rows in set (0.00 sec)
可以看到,在使用触发器之后,history表中已经成功的记录了test1表的记录的变更历史情况。
当导致触发器动作的语句存在于一个事务中时,触发器中的语句也执行在该事务中。当原始语句的事务回滚时,触发器中的语句的效果也将被回滚;当原始语句的事务提交时,触发器中的语句的效果也将被提交。
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into test1 values (‘003‘);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+-------+
| value |
+-------+
| 002 |
| 003 |
+-------+
2 rows in set (0.00 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
| 31 | A001B | NULL | 2017-08-29 11:51:20 |
| 38 | NULL | 003 | 2017-08-29 11:53:06 |
+----+-----------+-----------+---------------------+
6 rows in set (0.00 sec)
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test1;
+-------+
| value |
+-------+
| 002 |
+-------+
1 row in set (0.00 sec)
mysql> select * from history;
+----+-----------+-----------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-----------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
| 31 | A001B | NULL | 2017-08-29 11:51:20 |
+----+-----------+-----------+---------------------+
5 rows in set (0.00 sec)
在触发器中可以使用SET语句修改NEW.XXX的值,从而使得数据表中的新增行的数据成为修改后的值。
drop trigger if exists tr_p0;
delimiter $$
create trigger tr_p0 before insert
on test1 for each row
begin
set NEW.value = concat( ‘coe2coe:‘, NEW.value);
insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
end;
$$
delimiter ;
执行结果如下:
mysql> truncate table test1;
Query OK, 0 rows affected (0.03 sec)
mysql> truncate table history;
Query OK, 0 rows affected (0.02 sec)
mysql> insert into test1 values (‘001‘);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+-------------+
| value |
+-------------+
| coe2coe:001 |
+-------------+
1 row in set (0.00 sec)
mysql> select * from history;
+----+-----------+-------------+---------------------+
| id | old_value | new_value | time |
+----+-----------+-------------+---------------------+
| 3 | NULL | coe2coe:001 | 2017-08-29 13:07:56 |
+----+-----------+-------------+---------------------+
1 row in set (0.00 sec)
可以看到,数据表test1中新增的行的value字段的值由INSERT语句中的001变成了coe2coe_001,这就是触发器中的SET语句的作用。
触发器中可以调用不返回结果集的存储过程,但是不能调用返回结果集的存储过程。
以下存储过程不返回任何结果集,因此可以正常调用。
drop procedure if exists sp_p5;
delimiter $$
create procedure sp_p5()
begin
set @x=‘coe2coe@qq.com‘;
end;
$$
delimiter ;
触发器:
drop trigger if exists tr_p0;
delimiter $$
create trigger tr_p0 before insert
on test1 for each row
begin
insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
call sp_p5();
end;
$$
delimiter ;
执行结果:
mysql> insert into test1 values (‘coe2coe@qq.com‘);
Query OK, 1 row affected (0.00 sec)
mysql> select * from test1;
+----------------+
| value |
+----------------+
| 002 |
| 003 |
| coe2coe@qq.com |
+----------------+
3 rows in set (0.00 sec)
mysql> select * from history;
+----+-----------+----------------+---------------------+
| id | old_value | new_value | time |
+----+-----------+----------------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
| 31 | A001B | NULL | 2017-08-29 11:51:20 |
| 52 | NULL | 003 | 2017-08-29 12:01:54 |
| 59 | NULL | coe2coe@qq.com | 2017-08-29 12:03:32 |
+----+-----------+----------------+---------------------+
7 rows in set (0.00 sec)
如果将上述存储过程修改为返回结果集的存储过程,则不能在触发器中调用,否则在触发该触发器时将发生错误。此种情况下,在定义触发时并不会提示任何错误。
mysql> drop procedure if exists sp_p5;
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter $$
mysql>
mysql> create procedure sp_p5()
-> begin
-> select ‘coe2coe@qq.com‘;
-> end;
-> $$
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql>
mysql> drop trigger if exists tr_p0;
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter $$
mysql> create trigger tr_p0 before insert
-> on test1 for each row
-> begin
-> insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
-> call sp_p5();
-> end;
-> $$
Query OK, 0 rows affected (0.02 sec)
mysql> delimiter ;
使用以下语句触发该触发器,则提示错误。
mysql> select * from test1;
+----------------+
| value |
+----------------+
| 002 |
| 003 |
| coe2coe@qq.com |
+----------------+
3 rows in set (0.00 sec)
mysql> insert into test1 values (‘coe2coe‘);
ERROR 1415 (0A000): Not allowed to return a result set from a trigger
mysql> select * from test1;
+----------------+
| value |
+----------------+
| 002 |
| 003 |
| coe2coe@qq.com |
+----------------+
3 rows in set (0.00 sec)
mysql> select * from history;
+----+-----------+----------------+---------------------+
| id | old_value | new_value | time |
+----+-----------+----------------+---------------------+
| 3 | NULL | 001 | 2017-08-29 11:49:34 |
| 10 | NULL | 002 | 2017-08-29 11:50:02 |
| 17 | 001 | A001 | 2017-08-29 11:50:27 |
| 24 | A001 | A001B | 2017-08-29 11:50:58 |
| 31 | A001B | NULL | 2017-08-29 11:51:20 |
| 52 | NULL | 003 | 2017-08-29 12:01:54 |
| 59 | NULL | coe2coe@qq.com | 2017-08-29 12:03:32 |
+----+-----------+----------------+---------------------+
7 rows in set (0.00 sec)
从上述结果可以看到,在触发触发器时如果发生了错误,则导致触发器触发的原始INSERT语句也失败了,在test1表中并不会看到该INSERT新增的记录,history表也没有相关记录。
在一个触发器中,不能包含导致触发器的循环触发的语句。下面的触发器由test1表的BEFORE INSERT事件触发,而在该触发器中执行了一条test1表上的insert语句,这将导致触发器的循环触发,形成一个死循环。这种情况在定义触发器时虽然并不报错,但是在向test1表插入新数据时将报告一个错误。
mysql> delimiter $$
mysql> create trigger tr_p0 before insert
-> on test1 for each row
-> begin
-> insert into history (old_value,new_value,time) values (NULL, NEW.value, current_timestamp());
-> insert into test1 values ( concat(‘001_‘, NEW.value) );
-> end;
-> $$
Query OK, 0 rows affected (0.01 sec)
mysql> delimiter ;
mysql>
mysql> select * from test1;
Empty set (0.00 sec)
mysql> select * from history;
Empty set (0.00 sec)
mysql> insert into test1 values (‘coe2coe@qq.com‘);
ERROR 1442 (HY000): Can‘t update table ‘test1‘ in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
触发器的触发是基于数据表的行记录触发的,即每一行数据都会导致触发器的一次触发。当一条SQL语句导致大量行数据变化时,将会导致大量的触发器动作。这可能会造成比较验证的性能问题。
不恰当的使用触发器,还可能导致一些锁方面的问题。因此,除非非常必要,尽量在应用程序中完成相关业务的数据操作,而不是使用触发器。
MySQL基础知识09触发器
标签:基于 cte rollback values read top 引用 which strong