mysql如何临时禁用触发器

mysql如何临时禁用触发器

起因

??Mysql的触发器,在触发控制上,只能按照对数据的操作方式(Insert,Update,Delete)以及操作前后(before,after)进行触发控制。但是如果碰到以下需求又该如何:对于A表的Insert语句,只有符合某些条件的数据触发Insert触发器。

自己当初条件反射的写法

??在对应的触发器语句中,增加条件判断的逻辑。举个栗子:
有个用户信息表user,有个通讯录表addressbook,两张表表结构类似,业务需求上某些数据需要做数据实时同步,即user有更新,addressbook也要更新。用户表有个Insert触发器,只把性别为男的用户数据插入到addressbook中。
两表结构如下:

#为方便起见两个表结构就一模一样了,真实环境下一般都是部分字段类似而已
#性别字段取值1男0女
CREATE TABLE `user` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) NULL DEFAULT NULL,
    `sex` TINYINT(4) NULL DEFAULT ‘0‘,
    `age` INT(11) NULL DEFAULT ‘0‘,
    `phone` VARCHAR(50) NULL DEFAULT NULL,
    `qq` VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
;

CREATE TABLE `addressbook` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(50) NULL DEFAULT NULL,
    `sex` TINYINT(4) NULL DEFAULT ‘0‘,
    `age` INT(11) NULL DEFAULT ‘0‘,
    `phone` VARCHAR(50) NULL DEFAULT NULL,
    `qq` VARCHAR(50) NULL DEFAULT NULL,
    PRIMARY KEY (`id`)
)
;

此时user表的Insert触发器写法:

CREATE DEFINER=`root`@`localhost` TRIGGER `user_insert_trigger` BEFORE INSERT ON `user` FOR EACH ROW 

BEGIN
    if new.sex = 1
    then
        insert into addressbook (name,sex,age,qq,phone)
            values (new.name,new.sex,new.age,new.qq,new.phone)
        ;
    end if;

END

??经常码代码的程序猿们,应该最先想到的是这种写法吧,毕竟符合咱们平常写代码的逻辑。但是需求往往不是那么简单的,这头怪兽总是会向着我们不可预期的方向发展。再举个栗子:
现在只要同步这些用户:年龄大于30岁,qq号小于8位的,使用189手机的男用户。(卧槽(╯‵□′)╯︵┻━┻)。现在的触发器语句可能是这样子:

CREATE DEFINER=`root`@`localhost` TRIGGER `user_insert_trigger` BEFORE INSERT ON `user` FOR EACH ROW BEGIN
    if new.sex = 1 and  substring(new.phone,1,3)=189 and length(new.qq) <=8 and new.age >30
    then
        insert into addressbook (name,sex,age,qq,phone)
        values (new.name,new.sex,new.age,new.qq,new.phone)
             ;
    end if;
END

??不就是if多了几个and条件,多使用了几个sql函数而已嘛!这的是这样吗?如果手机的判断条件变成是“福建电信用户”(不止是189号头哦)呢?还有更奇葩的需求:如果要实现user表和addressbook表双向同步怎么办,没错,就是user表跟addressbook表都有insert触发器往对方表插数据,这在mysql下是不允许的(防止循环触发)。当时碰到这个需求我竟然脑袋一热也用上面的方法这么写,触发器插入前判断new的数据在要插入的表里存不存在orz。
总结一下,上面的触发器写法有很多缺点:

  1. 当对触发的数据有过滤需求时,需要用数据库语言写很多条件判断语句,有些在程序语言中很好实现的,在数据库语言中非常困难。
  2. 不利于理解与维护。由于有了1中那么多逻辑判断代码,导致触发器的语句会很冗长且复杂,阅读起来非常困难,即使加了注释。
  3. 不利于调试。数据库语言毕竟不像其他程序设计语言,你想打断点或者输出日志看是什么原因没有触发吗?图样,只能将insert数据插入到一张临时表里查看。
  4. 如果两张表有互相insert触发往对方表增加新数据的逻辑,这种写法要么不可行,要么作死。

更灵活优雅的写法

??方法是使用mysql的session级变量控制触发器触发,然后把上面的一堆判断逻辑放到程序代码中。这是在google上找到的stackoverflow帖子,感觉挺有用的。
举个栗子,上面的触发器可以这么写:

CREATE DEFINER=`root`@`localhost` TRIGGER `user_insert_trigger` BEFORE INSERT ON `user` FOR EACH ROW 

#这里@disable_triggers 是自定义的session变量(mysql中约定session变量用@开头,只对某一次会话有效,不影响其他会话),保证使用时各个触发器名字不一样就好
BEGIN
    IF @disable_triggers IS NULL THEN
        insert into addressbook (name,sex,age,qq,phone)
        values (new.name,new.sex,new.age,new.qq,new.phone)
        ;
    END IF;

END

??如果在使用insert语句时,不想触发触发器,在sql语句前后加上这么两句即可。

#变量设为非NULL,这样不会进入触发器的相关操作
SET @disable_triggers = 1;

#不打算触发触发器的insert语句
insert into user values(....);

#上面语句执行完毕完毕后,重新将变量设置为NULL,重新开启触发逻辑
SET @disable_triggers = NULL;

??对于某些更新数据时暂时想禁用触发器的情况,这种方法就可以先在程序代码中实现判断,然后决定要不要在执行的sql语句前后加入SET @disable_triggers = 1;SET @disable_triggers = NULL; 来临时禁止触发器触发操作,触发器的代码就可以做到非常简洁而且容易维护。将判断逻辑放到程序代码中也方便debug。

??不过在实践中发现此方法要注意以下几点(我使用的开发语言为java):

  1. SET @disable_triggers = 1; SET @disable_triggers = NULL; 这两个语句必须与要执行的sql放在同一条sql中,然后一起提交执行。不能先 执行 SET @disable_triggers = 1; 再执行数据更新语句,再执行SET @disable_triggers = NULL; ,因为使用的变量是session变量,只有在同一个会话中,触发器才能找到disable_triggers变量。如果分成三次提交执行,相当于三个会话,触发器那边得到的disable_triggers变量还是为NULL。
  2. 对于java中的 sql prepared statement 预处理执行sql语句的方式,以上设置session变量的方法会抛异常。(预处理模式不支持一次同时提交多个sql语句?)我在项目中使用的是spring 的jdbctemplate,原生jdbc应该也是一样的。所以使用这种方法临时禁用触发器时,还是老实做好参数防注入判断,然后拼接sql语句执行吧,还好这种需求的场景不会很多。
时间: 2024-10-03 14:45:04

mysql如何临时禁用触发器的相关文章

mysql临时禁用触发器

mysql支持设定session变量,并且有带入到触发器中使用的能力,故可以间接的设置触发器失效 思路是: 在执行前设定一个session变量,执行过程中判断该变量的值(没有设定该变量的值时该变量默认值为null) 例: set @disable_triggers = 1; UPDATE std_importpc pc  JOIN std_import_checked chk on pc.TrademarketCode in ('0','1') and chk.Texch = '.OF' and

mysql 自定义存储过程和触发器

mysql 自定义存储过程和触发器 --存储过程示范 DROP PROCEDURE IF EXISTS PRO_TEST; CREATE PROCEDURE PRO_TEST(IN NUM_IN INT,OUT NUM_OUT INT,INOUT NUM_INOUT) BEGIN //DO ANYTHING YOU WANT END; 参数解释: in : 就是输入参数,输入参数是会被传入到存储过程作为参数使用,改变它的值将不会改变其原本值,相当于是值传递 out: 此为输出参数,在存储过程中为

MySQL与SQL的触发器的不同写法

触发器作用:当在SQL.MySQL数据库中一张表中插入一条记录时,触动触发器,使同一数据库的另一张表插入相同记录. 在学习的过程中,发现两者的写法是不同的. 1.在SQL中建立两张表sql_test,sql_tem.(两张表的字段是一样的) 代码如下: create table sql_test ( id int, name varchar(16) ) create table sql_tem ( id int, name varchar(16) ) 代码如下: /*SQL的触发器写法*/ cr

MYSQL数据库学习八 触发器的操作

8.1 触发器 在表发生更改时,自动进行一些处理.例如,学生表中每增加一条关于学生记录时,学生的总数就必须同时改变,同时需要检查电话号码格式是否正确,地址缩写是否正确. 以下语句会激活触发器: DELETE语句. INSERT语句. UPDATE语句. MySQL 5开始支持触发器数据库对象. 8.2 创建触发器 CREATE TRIGGER trigger_name BEFORE|AFTER trigger_event ON table_name FOR EACH ROW trigger_st

MySQL之试图、触发器、事务、存储过程、函数

阅读目录 一.视图 二.触发器 三.事务 四.存储过程 五.函数 六.流程控制 一.视图 视图是一个虚拟表(非真实存在),是跑到内存中的表,真实表是硬盘上的表,怎么就得到了虚拟表,就是你查询的结果,只不过之前我们查询出来的虚拟表,从内存中取出来显示在屏幕上,内存中就没有了这些表的数据,但是下次我要是想用这个虚拟表呢,没办法,只能重新查一次,每次都要重新查.其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用.如果我们想查询一

MySQL之视图、触发器、事务、存储过程、函数 流程控制

阅读目录 一 视图 二 触发器 三 事务 四 存储过程 五 函数 六 流程控制 MySQL这个软件想将数据处理的所有事情,能够在mysql这个层面上全部都做了,也就是说它想要完成一件事,以后想开发的人,例如想写python程序的人,你就专门的写你自己的python程序,以后凡是关于数据的增删改查,全部都在MySQL里面完成,也就是说它想实现一个数据处理与应用程序的一个完全的解耦和状态,比如说,如果我是个应用程序员,我想要查询数据,我不需要自己写sql语句,只需要调用mysql封装好的一些功能,直

Mysql 之 视图,触发器,存储过程,函数,事物,数据库锁,数据库备份

Mysql 之 视图,触发器,存储过程,函数,事物,数据库锁,数据库备份 阅读目录 一:视图 二:触发器 三:存储过程 四:函数 五:事物 六:数据库锁 七:数据库备份 一:视图 视图:是一个虚拟表,其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据 视图有如下特点; 1. 视图的列可以来自不同的表,是表的抽象和逻辑意义上建立的新关系. 2. 视图是由基本表(实表)产生的表(虚表). 3. 视图的建立和删除不影响基本表. 4. 对视图内容的更新(添加.删除和修改)直接影响基本表

mysql之视图,触发器,事务等。。。

mysql之视图,触发器,事务等... 一.视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名称]即可获取结果集,可以将该结果集当做表来使用. 使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显地效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推

mysql 启动事件 启动触发器功能

事件调度器有时也可称为临时触发器(temporal triggers), 因为事件调度器是基于特定时间周期触发来执行某些任务,而触发器(Triggers)是基于某个表所产生的事件触发的,区别也就在这里. 在使用这个功能之前必须确保event_scheduler已开启,可执行 开启: ①(开启事件触发器) SET GLOBAL event_scheduler = 1; 或我们可以在配置my.ini文件 中加上 event_scheduler = 1 ②或 SET GLOBAL event_sche