当前位置:Gxlcms > 数据库问题 > MySQL基础知识09触发器

MySQL基础知识09触发器

时间:2021-07-01 10:21:17 帮助过:12人阅读

2. 触发器的理解

3. 触发器的例子

3.1. 触发器的使用的例子

3.2. 触发器和事务的例子

3.3. 在触发器中修改所在行的数据

4. 触发器注意事项

4.1. 触发器不能调用返回结果集的存储过程

4.2. 触发器中不能导致循环触发的语句

4.3. 触发器的效率问题

 

1. 触发器的语法结构

触发器的语法如下:

 

CREATE TRIGGER trigger_name trigger_time trigger_event

    ON tbl_name FOR EACH ROW trigger_stmt

 

其中,trigger_time的值为BEFORE或者AFTER,表示触发器的语句在激活该触发器的语句之前或之后执行。

trigger_event指定激活该触发器的语句的类型,包括以下几种:

INSERT:将新行插入到数据表中,包括INSERTLOAD DATA REPLACE语句。

UPDATE:修改现存行的数据,包括UPDATE语句。

DELETE:删除现存数据,包括DELETEREPLACE语句。

 

 

2. 触发器的理解

 

对于同一个数据表的同一个事件和同一个时间,不能有两个触发器。比如,对于数据表table1,不可以有两个BEFORE INSERT触发器,也不能有两各AFTER INSERT触发器。但是允许有一个BEOFRE INSERT触发器和一个AFTER INSERT触发器同时存在。

 

在触发器中,使用别名OLDNEW分别引用动作发生时的原有记录数据和新记录数据。

事件

OLD(只读)

NEW(可使用SET修改)

BEFORE INSERT

无效

将要插入的数据

AFTER INSERT

无效

已经插入的数据

BEFORE UPDATE

将要被修改的原始数据

将要代替原始数据的新数据

AFTER UPDATE

已经被修改的原始数据

已经代替了原始数据的新数据

BEFORE DELETE

将要被删除的原始数据

无效

AFTER DELETE

已经被删除的原始数据

无效

 

如果在触发器中使用了无效的OLDNEW,则在创建触发器时会报错。

 

(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事件的触发器。

 

3. 触发器的例子

 

3.1. 触发器的使用的例子

 

 

假定存在以下数据表:

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表的记录的变更历史情况。

 

 

3.2. 触发器和事务的例子

 

当导致触发器动作的语句存在于一个事务中时,触发器中的语句也执行在该事务中。当原始语句的事务回滚时,触发器中的语句的效果也将被回滚;当原始语句的事务提交时,触发器中的语句的效果也将被提交。

 

 

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)

 

 

 

3.3. 在触发器中修改所在行的数据

在触发器中可以使用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语句的作用。

 

4. 触发器注意事项

 

4.1. 触发器不能调用返回结果集的存储过程

触发器中可以调用不返回结果集的存储过程,但是不能调用返回结果集的存储过程。

 

以下存储过程不返回任何结果集,因此可以正常调用。

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表也没有相关记录。

 

 

4.2. 触发器中不能导致循环触发的语句

 

在一个触发器中,不能包含导致触发器的循环触发的语句。下面的触发器由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.

 

 

 

4.3. 触发器的效率问题

 

 

触发器的触发是基于数据表的行记录触发的,即每一行数据都会导致触发器的一次触发。当一条SQL语句导致大量行数据变化时,将会导致大量的触发器动作。这可能会造成比较验证的性能问题。

不恰当的使用触发器,还可能导致一些锁方面的问题。因此,除非非常必要,尽量在应用程序中完成相关业务的数据操作,而不是使用触发器。

 

MySQL基础知识09触发器

标签:基于   cte   rollback   values   read   top   引用   which   strong   

人气教程排行