本文讨论语句定界符(delimiter)和触发器(trigger)的应用。
前面的示例中,执行的每条SQL语句后都有一个分号(;),它是语句的结束符,也是定界符,标识了SQL语句的结束;默认情况下,SQL语句会自动提交(auto commit)执行,即当SQL语句遇到分号时就会立即执行。
在一些情况下,如创建触发器、存储过程、事务等数据库元素时,可能需要包含多条语句,这些语句会作为整体执行;此时,可以通过delimiter语句重新定义定界符,新的定界符只要不是SQL语句中使用的符号即可,如使用$$或//等符号;创建对象代码中依然可以将分号作为语句的结束符,但只有在遇到新的定界符时才会提交执行;对象创建完成后应使用delimiter语句重新将定界符定义为分号(;)。接下来,在创建触发器时可以看到定界符的具体应用。
触发器是在特定事件时自动执行操作的一种工作机制,可以完成一些数据自动化处理工作。创建触发器使用create trigger语句,基本语法如下:
create trigger [if not exists] <触发器名称> <执行时间> <执行事件> on <表名> for each row <执行语句>
其中:
使用insert触发器时可以使用new临时表读取添加的数据,对于auto_increment字段,在before触发时为0,在after触发时为新的数值。下面的代码会在cdb_demo数据库中创建t200和t201表,其中,t200表为数据原始表,t201表用于保存t200表新添加数据的副本。
use cdb_demo; create table t200( recid bigint not null auto_increment primary key, f1 varchar(30) not null unique, f2 varchar(30) not null, f3 int )engine=innodb default charset='utf8mb4'; create table t201( recid bigint not null, f1 varchar(30) not null, f2 varchar(30) not null, f3 int )engine=innodb default charset='utf8mb4'; create index ind_t201_f1 on t201(f1);
代码中首先创建了t200表,然后创建的t201表结构并不完全一样,主要的区别在于t201表中的f1字段没有定义为主键,f2字段没有定义唯一约束,但添加了索引;这么做的原因是t200表中的主键和唯一键数据可能会删除后重新添加相同的数据,此时再添加到t201表就会产生主键或唯一约束错误。实际应用中,也可以在副本数据的表中添加一些附加数据字段,如操作用户、操作时间和操作的客户端IP等信息。
下面的代码会在t200表中添加insert触发器。
use cdb_demo; delimiter // create trigger trig_t200_insert_a after insert on t200 for each row begin insert into t201(recid,f1,f2,f3) values(new.recid,new.f1,new.f2,new.f3); end; // delimiter ;
本代码创建了trig_t200_insert_a触发器,它会在t200表执行insert语句之后将新添加的数据同步保存到t201表。下面的代码会在t200表中添加一条记录。
use cdb_demo; insert into t200(f1,f2,f3) values('user01','用户01',1);
执行语句后,可以查询t201表中是否同步了添加的数据,如下图所示。
使用update触发器时可以使用new临时表读取或修改新的数据,使用old临时表读取修改前的数据。下面的代码会创建t202表,用于保存t200表数据更新前的旧数据和更新后的新数据。
use cdb_demo; create table t202( recid bigint not null, f1 varchar(30) not null, f2 varchar(30) not null, f3 int, f_ts timestamp default current_timestamp )engine=innodb default charset='utf8mb4'; create index ind_t202_recid on t202(recid);
这里,在t202表中添加了f_ts字段,用于保存记录添加时的系统时间;通过recid和f_ts字段数据的顺序就可以完全跟踪t200表数据的修改情况。下面的代码创建了t200表中的update触发器。
use cdb_demo; delimiter // create trigger trig_t200_update_b before update on t200 for each row begin insert into t202(recid,f1,f2,f3) values(old.recid,old.f1,old.f2,old.f3); end; create trigger trig_t200_update_a after update on t200 for each row begin insert into t202(recid,f1,f2,f3) values(new.recid,new.f1,new.f2,new.f3); end; // delimiter ;
代码中创建了t200表update操作的两个触发器,在更新之前会将旧的数据保存到t202表中,在更新之后会将新的数据保存到t202表中。
下面的代码会更新t200表中f1字段数据为'user01'的记录。
use cdb_demo; update t200 set f2='张三' where f1='user01';
执行t200表的更新操作后,可以在t202表中查看更新前后的数据对比,如下图的所示。
使用delete触发器时可以使用old临时表读取删除的数据,并将删除数据保存到指定的表中。下面的代码会创建t203表,用于保存t200表中删除的数据副本。
use cdb_demo; create table t203( recid bigint not null, f1 varchar(30) not null, f2 varchar(30) not null, f3 int, f_ts timestamp default current_timestamp )engine=innodb default charset='utf8mb4'; create index ind_t203_recid on t203(recid);
这里创建的t203表结构与t202表相同,同样也添加了recid字段的索引。下面的代码会创建t200表的delete触发器。
use cdb_demo; delimiter // create trigger trig_t200_delete_b before delete on t200 for each row begin insert into t203(recid,f1,f2,f3) values(old.recid,old.f1,old.f2,old.f3); end; // delimiter ;
本例,trig_t200_delete_b触发器会在t200表执行delete操作前将删除的数据保存到t203表中。
下面的代码将删除t200表中f1字段数据为'user01'的记录。
use cdb_demo; delete from t200 where f1='user01';
执行操作后可以查看t203表的记录,如下图所示。
上述示例中,将insert、update、delete三种触发器的数据副本分别保存到不同的数据表中,实际应用中,如果将所有数据副本保存到一个表中,可以在表中添加操作类型字段,比如,可以将触发器类型作为操作标识数据。
删除触发器时可以使用drop trigger语句,格式如下:
drop trigger <触发器名称>;
需要注意,如果insert、update或delete操作失败,则对应的after触发器并不会执行。如果before触发器执行失败,则不会执行主语句和after触发器。