MySQL(十)------ 觸發器

      觸發器是與表有關的數據庫對象,在滿足定義的條件時觸發,然後執行觸發器中定義的語句集合,這種特性可以協助應用在數據庫端確保數據的完整性。MySQL在5.02版本後開始支持該功能。

一、創建觸發器

      語法如下:

CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt
  • trigger_name:觸發器的名字;
  • trigger_time:觸發器的觸發時間,有兩個取值,BEFORE或AFTER;
  • trigger_event:觸發的事件,取值爲INSERT(向表中插入新行時觸發,例如INSERT、LOAD DATA、REPLACE等操作都會插入新行)、UPDATE(更新某一行時觸發)、DELETE(刪除某一行時觸發,如DELETE、REPLACE);
  • trigger_stmt:觸發器觸發後執行的操作語句,如果語句較多可以使用BEGIN....END語句塊包裹起來。

注意:觸發器只能創建在永久表上,不能對臨時表創建觸發器。

        對同一個表的相同出發時間相同觸發事件只能定義一個觸發器,可以使用別名OLD和NEW來引用觸發器中發生變化的記錄內容。下面舉個例子來簡單認識以下觸發器:

1. 首先創建兩張表,裏面暫無數據
mysql> create table film(film_id int auto_increment primary key,title char(10),description varchar(20));
Query OK, 0 rows affected (0.02 sec)

mysql> create table film_text(film_id int auto_increment primary key,title char(10),description varchar(20));
Query OK, 0 rows affected (0.01 sec)

2. 對film表創建一個觸發器,作用是當film表有新的數據插入時film_text表也會進行相應操作
mysql> delimiter $$
mysql>
mysql> create trigger ins_film after insert on film for each row
    -> begin
    -> insert into film_text values(new.film_id,new.title,new.description);
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> delimiter ;

3. 向film表插入記錄同時查看film_text表的變化
mysql> insert into film values(1,'ACADEMY','A E pic drama of...');
Query OK, 1 row affected (0.00 sec)

mysql> select * from film;
+---------+---------+---------------------+
| film_id | title   | description         |
+---------+---------+---------------------+
|       1 | ACADEMY | A E pic drama of... |
+---------+---------+---------------------+
1 row in set (0.00 sec)

mysql> select * from film_text;
+---------+---------+---------------------+
| film_id | title   | description         |
+---------+---------+---------------------+
|       1 | ACADEMY | A E pic drama of... |
+---------+---------+---------------------+
1 row in set (0.00 sec)
可以看到對film表插入數據時觸發了觸發器,對film_text表同樣插入了相應的數據

            這裏有一點值得說明的是,有時候同一個觸發器可能被不同的事件觸發,比如上面提到的INSERT、LOAD DATA操作都可以觸發插入事件;另外,多個觸發器對某一事件都會觸發時觸發的順序也會讓人疑惑,比如這個語句 INSERT INTO ... ON DUPLICATE KEY UPDATE 插入時如果遇到重複值則進行更新操作,這裏有可能是插入操作也有可能是更新操作,因此可能觸發的觸發器會比較多,比如下面的一個例子:

1. 創建四個觸發器
mysql> delimiter $$
mysql>
mysql> create trigger ins_film_bef before insert on film for each row
    -> begin
    -> insert into film_text(title) values('before insert');
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> create trigger ins_film_aft after insert on film for each row
    -> begin
    -> insert into film_text(title) values('after insert');
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> create trigger upd_film_bef before update on film for each row
    -> begin
    -> insert into film_text(title) values('before update');
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> create trigger upd_film_aft after update on film for each row
    -> begin
    -> insert into film_text(title) values('after update');
    -> end $$
Query OK, 0 rows affected (0.01 sec)

mysql> delimiter ;

2. 對上面創建的film_text表進行些許修改
mysql> delete from film_text;
Query OK, 1 row affected (0.00 sec)

mysql> alter table film_text drop description;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

3. 插入記錄已經存在的情況
mysql> insert into film values(1,'onlytest','test for trigger') on duplicate key update title='update';
Query OK, 2 rows affected (0.01 sec)

mysql> select * from film_text;
+---------+---------------+
| film_id | title         |
+---------+---------------+
|       2 | before insert |
|       3 | before update |
|       4 | after update  |
+---------+---------------+
3 rows in set (0.00 sec)

4. 插入新記錄的情況
mysql> delete from film_text;
Query OK, 3 rows affected (0.00 sec)

mysql> insert into film values(2,'test','trigger') on duplicate key update title='update';
Query OK, 1 row affected (0.01 sec)

mysql> select * from film_text;
+---------+---------------+
| film_id | title         |
+---------+---------------+
|      13 | before insert |
|      14 | after insert  |
+---------+---------------+
2 rows in set (0.00 sec)

         從這個例子可以看出觸發器的執行順序,對於已有數據無法重複插入而執行更新操作時,BEFORE INSERT、BEFORE UPDATE、AFTER UPDATE觸發器被觸發;而僅僅是執行簡單的插入的話BEFORE INSERT、AFTER INSERT觸發器被觸發;因此,在寫此類觸發器的時候要注意這種情況,避免錯誤的觸發觸發器。

二、刪除觸發器

      刪除觸發器語法:

      DROP TRIGGER [schema_name.]trigger_name        如果不指定schema_name則默認爲當前數據庫

mysql> drop trigger ins_film;
Query OK, 0 rows affected (0.01 sec)

三、查看觸發器

       查看觸發器一般有兩種方法:SHOW TRIGGERS和查詢系統表information_schema.trigger;

使用命令show triggers:

使用命令show triggers
mysql> show triggers \G
*************************** 1. row ***************************
             Trigger: ins_film_bef
               Event: INSERT
               Table: film
           Statement: begin
insert into film_text(title) values('before insert');
end
              Timing: BEFORE
             Created: 2018-12-24 11:21:42.42
            sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: gbk
collation_connection: gbk_chinese_ci
  Database Collation: utf8_general_ci
*************************** 2. row ***************************
             Trigger: ins_film_aft
               Event: INSERT
               Table: film
           Statement: begin
insert into film_text(title) values('after insert');
end
              Timing: AFTER
             Created: 2018-12-24 11:22:50.75
            sql_mode: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
             Definer: root@localhost
character_set_client: gbk
collation_connection: gbk_chinese_ci
  Database Collation: utf8_general_ci
。。。。。。

            這種方法可以查看到觸發器的狀態、語法等信息,但它會查出所有的觸發器,不能指定單獨的,所以不是很方便。

使用系統表命令:

mysql> desc information_schema.triggers;
+----------------------------+---------------+------+-----+---------+-------+
| Field                      | Type          | Null | Key | Default | Extra |
+----------------------------+---------------+------+-----+---------+-------+
| TRIGGER_CATALOG            | varchar(512)  | NO   |     |         |       |
| TRIGGER_SCHEMA             | varchar(64)   | NO   |     |         |       |
| TRIGGER_NAME               | varchar(64)   | NO   |     |         |       |
| EVENT_MANIPULATION         | varchar(6)    | NO   |     |         |       |
| EVENT_OBJECT_CATALOG       | varchar(512)  | NO   |     |         |       |
| EVENT_OBJECT_SCHEMA        | varchar(64)   | NO   |     |         |       |
| EVENT_OBJECT_TABLE         | varchar(64)   | NO   |     |         |       |
| ACTION_ORDER               | bigint(4)     | NO   |     | 0       |       |
| ACTION_CONDITION           | longtext      | YES  |     | NULL    |       |
| ACTION_STATEMENT           | longtext      | NO   |     | NULL    |       |
| ACTION_ORIENTATION         | varchar(9)    | NO   |     |         |       |
| ACTION_TIMING              | varchar(6)    | NO   |     |         |       |
| ACTION_REFERENCE_OLD_TABLE | varchar(64)   | YES  |     | NULL    |       |
| ACTION_REFERENCE_NEW_TABLE | varchar(64)   | YES  |     | NULL    |       |
| ACTION_REFERENCE_OLD_ROW   | varchar(3)    | NO   |     |         |       |
| ACTION_REFERENCE_NEW_ROW   | varchar(3)    | NO   |     |         |       |
| CREATED                    | datetime(2)   | YES  |     | NULL    |       |
| SQL_MODE                   | varchar(8192) | NO   |     |         |       |
| DEFINER                    | varchar(93)   | NO   |     |         |       |
| CHARACTER_SET_CLIENT       | varchar(32)   | NO   |     |         |       |
| COLLATION_CONNECTION       | varchar(32)   | NO   |     |         |       |
| DATABASE_COLLATION         | varchar(32)   | NO   |     |         |       |
+----------------------------+---------------+------+-----+---------+-------+
22 rows in set (0.00 sec)
mysql> select * from information_schema.triggers where trigger_name = 'ins_film_bef' \G;
*************************** 1. row ***************************
           TRIGGER_CATALOG: def
            TRIGGER_SCHEMA: test1
              TRIGGER_NAME: ins_film_bef
        EVENT_MANIPULATION: INSERT
      EVENT_OBJECT_CATALOG: def
       EVENT_OBJECT_SCHEMA: test1
        EVENT_OBJECT_TABLE: film
              ACTION_ORDER: 1
          ACTION_CONDITION: NULL
          ACTION_STATEMENT: begin
insert into film_text(title) values('before insert');
end
        ACTION_ORIENTATION: ROW
             ACTION_TIMING: BEFORE
ACTION_REFERENCE_OLD_TABLE: NULL
ACTION_REFERENCE_NEW_TABLE: NULL
  ACTION_REFERENCE_OLD_ROW: OLD
  ACTION_REFERENCE_NEW_ROW: NEW
                   CREATED: 2018-12-24 11:21:42.42
                  SQL_MODE: STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
                   DEFINER: root@localhost
      CHARACTER_SET_CLIENT: gbk
      COLLATION_CONNECTION: gbk_chinese_ci
        DATABASE_COLLATION: utf8_general_ci
1 row in set (0.02 sec)

四、觸發器的使用

      觸發器執行的語句有以下兩個限制:

  • 觸發器不能調用將數據返回客戶端的存儲程序,也不能使用採用CALL語句的動態SQL語句,但是允許存儲過程或函數通過OUT或者INOUT類型的參數將數據返回觸發器;
  • 不能在觸發器中使用以顯示或隱式方式開始或結束事務的語句,如START TRANSACTION、COMMIT或ROLLBACK。

          MySQL觸發器按照before觸發器、行操作、after觸發器的順序執行,其中任何一個步驟出錯後面的都不會執行,如果是對一個事務表的操作那麼一旦出錯整個操作會作爲一個事務被回滾;但如果是非事務表的操作,那麼已經更新的記錄將無法回滾,因此這一點需要非常注意!

       觸發器過多容易出錯而且性能也會下降,因此要合理使用,不要將過多的處理邏輯依賴觸發器。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章