###原由 Mysql的觸發器,在觸發控制上,只能按照對數據的操做方式(Insert,Update,Delete)以及操做先後(before,after)進行觸發控制。可是若是碰到如下需求又該如何:對於A表的Insert語句,只有符合某些條件的數據觸發Insert觸發器。java
###本身當初條件反射的寫法 在對應的觸發器語句中,增長條件判斷的邏輯。舉個栗子: 有個用戶信息表user,有個通信錄表addressbook,兩張表表結構相似,業務需求上某些數據須要作數據實時同步,即user有更新,addressbook也要更新。用戶表有個Insert觸發器,只把性別爲男的用戶數據插入到addressbook中。 兩表結構以下:mysql
#爲方便起見兩個表結構就如出一轍了,真實環境下通常都是部分字段相似而已 #性別字段取值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觸發器寫法:spring
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手機的男用戶。(臥槽(╯‵□′)╯︵┻━┻)。如今的觸發器語句多是這樣子:sql
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。 總結一下,上面的觸發器寫法有不少缺點:數據庫
###更靈活優雅的寫法 方法是使用mysql的session級變量控制觸發器觸發,而後把上面的一堆判斷邏輯放到程序代碼中。這是在google上找到的stackoverflow帖子,感受挺有用的。 舉個栗子,上面的觸發器能夠這麼寫:session
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。google
不過在實踐中發現此方法要注意如下幾點(我使用的開發語言爲java):debug
SET @disable_triggers = 1;
SET @disable_triggers = NULL;
這兩個語句必須與要執行的sql放在同一條sql中,而後一塊兒提交執行。不能先 執行 SET @disable_triggers = 1;
再執行數據更新語句,再執行SET @disable_triggers = NULL;
,由於使用的變量是session變量,只有在同一個會話中,觸發器才能找到disable_triggers變量。若是分紅三次提交執行,至關於三個會話,觸發器那邊獲得的disable_triggers變量仍是爲NULL。