目录
触发器有什么用?
当一个数据库中某个关系表的数据被更新时,其他与之相关的表的数据可能也会需要更新。手动去修改那些需要更新的表的数据会比较枯燥而繁琐。而有了触发器,当一个表的数据被更新时,会启用对应的触发器来自动更新那些与之相关的表的数据,省时又省力。下面讨论如何创建触发器。
创建触发器
我们先来研究一下创建触发器的语法:
CREATE
[DEFINER={user | CURRENT_USER}]
TRIGGER trigger_name
trigger_time trigger_event
ON table_name FOR EACH ROW
trigger_body
对于上面这组语法,方括号里的内容[DEFINER={user | CURRENT_USER}] 的用法我们先不讨论。
trigger_name: 触发器的名字,自己取一个,但记住不可以取已经存在的触发器的名字,会报错。
trigger_time: 一般有两种选择,BEFORE或者AFTER,表示触发器在下一条提到的事件发生前或者发生后执行。
trigger_event: 一般有三种选择,INSERT、DELETE和UPDATE,表示的是触发该触发器的事件。
trigger_body: 触发器触发后执行的指令。
下面举个实际应用的例子:
假如有两张关系表,一张叫worker_info,用来存储公司员工的信息;一张叫worker_count用来存储公司的员工总数。然后要求写几个触发器,用来处理以下情况:在把公司新来的员工信息登记到worker_info后,worker_count的数据也要相应的更新。
做法很简单,首先创建个测试用的库:
CREATE DATABASE test;
然后,写两张用作测试的表,worker_info和worker_count:
CREATE TABLE worker_info(
w_no INT(5) NOT NULL AUTO_INCREMENT,
w_name VARCHAR(10) DEFAULT NULL,
PRIMARY KEY(w_no)
);
CREATE TABLE worker_count(
w_count INT(20) DEFAULT 0
);
INSERT INTO worker_count VALUE(0);
好,开始写触发器,一个在往worker_info添加数据时触发,另一个在从worker_info删除数据时触发:
CREATE TRIGGER trigger_worker_info_insert
AFTER INSERT
ON worker_info FOR EACH ROW
UPDATE worker_count SET w_count=w_count+1;
CREATE TRIGGER trigger_worker_info_delete
AFTER DELETE
ON worker_info FOR EACH ROW
UPDATE worker_count SET w_count=w_count-1;
测试一下,
INSERT INTO worker_info VALUE(NULL,'John'),(NULL,'Mark'),(NULL,'Alex');
SELECT * FROM worker_count;
成功,下面来测试下删除数据:
DELETE FROM worker_info WHERE w_name IN ('John','Alex');
SELECT * FROM worker_count;
这时候可能你就要想了,我们讨论的这个例子就只在trigger_body这部分用了一条执行语句啊,万一一条不够用,我们能不能多用几条?
当然可以,下面就来讨论包含多条执行语句的触发器如何实现。
如何实现包含多条执行语句的触发器
还是一样我们先来看看创建包含多条执行语句的触发器的语法:
CREATE
[DEFINER={user | CURRENT_USER}]
TRIGGER trigger_name
trigger_time trigger_event
ON table_name FOR EACH ROW
BEGIN
trigger_statments
END;
大多数地方都与前面提到过的语法一样。不同之处是,在执行多条语句时,我们需要BEGIN和END来作为开始和结束的标志。
那么现在我们用这个触发器的性质来解决一下实际问题,
假如我在worker_count表和worker_info同时增加个字段,w_dept_no,表示员工所在的部门编号,0表示所有部门,1表示部门1,2表示部门2,那么现在如果要在worker_info中添加员工信息,这个触发器的创建比之前的触发器要复杂一点。有点挑战性,咱们开始写代码:
首先得清空两张表的数据,并且删除两个前面创建的触发器(不删除的话,会导致work_count表的数据不准确):
删除触发器的语法是:
DROP TRIGGER trigger_name;
于是我们执行以下代码:
DELETE FROM worker_info;
DELETE FROM worker_count;
DROP TRIGGER trigger_worker_info_insert;
DROP TRIGGER trigger_worker_info_delete;
然后添加字段w_dept_no,两张表都要添加:
ALTER TABLE worker_info ADD w_dept_no INT(10);
ALTER TABLE worker_count ADD w_dept_no INT(10);
下面开始写触发器:
DELIMITER $$
CREATE TRIGGER trigger_worker_info_insert
AFTER INSERT
ON worker_info FOR EACH ROW
BEGIN
UPDATE worker_count SET w_count=w_count+1 WHERE w_dept_no=0;
UPDATE worker_count SET w_count=w_count+1 WHERE w_dept_no=NEW.w_dept_no;
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER trigger_worker_info_delete
AFTER DELETE
ON worker_info FOR EACH ROW
BEGIN
UPDATE worker_count SET w_count=w_count-1 WHERE w_dept_no=0;
UPDATE worker_count SET w_count=w_count-1 WHERE w_dept_no=OLD.w_dept_no;
END$$
DELIMITER ;
创建完触发器后,初始化一下worker_count表:
INSERT INTO worker_count VALUE(0,0),(0,1),(0,2);
开始插入数据到worker_info:
INSERT INTO worker_info VALUES(NULL,'Jack',2),(NULL,'Bob',1),(NULL,'Carror',2),(NULL,'Dave',1),(NULL,'Brown',1),(NULL,'Alex',2);
很成功,再来试一下从worker_info删除数据:
DELETE FROM worker_info WHERE w_name='Carror';
下面来讨论一下,
大家可能想了解DELIMITER的作用,DELIMITER用于重新定义结束标识符。为什么要用这个东西呢?因为“;”是默认的语句结束标识符,一旦检测到这个标识符,程序就被认为结束了。在BEGIN和END语句块中,我们需要将语句的默认标识符设置成除了“;”之外的符号,这样就不至于只执行完第一句执行语句程序就终止。但是最后别忘了把默认标识符改回原来的“;”。
大家可能还注意到了,上面创建触发器的代码中NEW和OLD这两个关键字。
NEW关键字在触发器执行INSERT事件的时候有效,表示当前正在插入的数据(如例子中正在插入worker_info表的新的w_dept_no数据);OLD则在触发器执行DELETE事件时有效,表示当前正在删除的数据(如例子中正在被从worker_info表删除的'carror'对应的w_dept_no数据)。
如何查看已创建的触发器
如果需要查看已经创建了哪些触发器,则可以使用语句:
SHOW TRIGGERS;
我电脑里执行的的结果是这样的...(这是已经把命令行设置成全屏显示了..)
如果要查看更加详细的触发器结构,可以用以下语句:
USE information_schema;
SELECT * FROM triggers WHERE trigger_name=触发器名称;
拿前面最后一次创建的trigger_worker_info_insert来举例:
USE information_schema;
SELECT * FROM triggers WHERE trigger_name='trigger_worker_count_insert';
执行结果:(也是命令行设置成全屏显示的情况下)
得去换个屏幕更大的笔记本了...