MySQL觸發器的用處還是非常多地,關鍵看業務需要,曾經給大家介紹過基於存儲引擎MEMORY加觸發器的應用場景之一剖析。通過閱讀本文,將會告訴大家:觸發器的語法知識、觸發器的限制、審計案例分析和實現,將逐一講解。
語法
CREATE [DEFINER = { user | CURRENT_USER }] TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt |
DEFINER:指定觸發器的創建者,默認爲登錄mysqld服務器的賬號信息;
trigger_name:觸發器的名稱,要符合mysql對待數據庫對象命名的規範;
trigger_time:觸發表上的觸發器語句體執行的時間:行更新前還是行更新後,2個選項值:
BEFOR or AFTER;
tbl_name:指定觸發器是對應那一個數據庫對象:表的;
trigger_stmt:爲觸發器內部可執行的語句體;
觸發器限制
- 擁有觸發器的數據庫對象必須爲實體表,不能爲臨時表或視圖;
- MyISAM、MEMORY、InnoDB等常用存儲引擎都支持觸發器功能;
- 觸發器支持INSERT類操作:INSERT、LOAD DATA、REPLACE;
- 觸發器支持UPDATE操作;
- 觸發器支持DELETE操作,但是不支持DROP TABLE 、TRUNCATE操作;
- 處發起能支持字句:INSERT INTO … ON DUPLICATE KEY UPDATE …;
- 一個數據庫中的對象表,不能對同一觸發事件有2個或以上的觸發器同時響應;
- 一個數據庫中不能有同名的觸發器程序;
- 觸發器無法顯示調用執行,也無法像函數或存儲過程一樣顯示地傳遞參數;
- 通過關鍵字OLD.column_name獲得的值不能通過SET命令修改,但是關鍵字NEW獲得的值能通過SET NEW.column_name=VALUE方式修改;
- 觸發器的處理部分不能含有事務的關鍵字,例如:COMMIT、ROLLBACK等;
- 創建了觸發器的表,若支持事務,則觸發器也會受事務執行成功還是失敗的影響,且觸發器程序執行成功還是失敗,也會影響事務的執行是成功還是失敗;若不支持事務,則也無法支持事務的回滾操作;
審計案例
有一張存儲車輛收費信息的表t_car,因業務要求,程序要有對該表的數據修改權限,爲此需要審計對該表上數據的記錄值修改信息,以備查詢、跟蹤。表t_car結構:
CREATE TABLE t_car( `ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, `car_number` VARCHAR(45) DEFAULT ” COMMENT ‘車牌號’, `card_number` VARCHAR(45) DEFAULT ” COMMENT ‘卡片編號’, `pay` DECIMAL(6,1) DEFAULT 0 COMMENT ‘金額’, PRIMARY KEY(`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
審計存儲的內容:
修改前pay字段的值;
修改後pay字段的值;
記錄被修改的時間;
登陸數據庫服務器修改數據的ip地址、帳號信息;
爲此審計表的結構爲:
CREATE TABLE t_record( `ID` INT UNSIGNED NOT NULL AUTO_INCREMENT, `username` VARCHAR(45) DEFAULT ” COMMENT ‘登錄mysql的用戶名’, `client_ip` VARCHAR(45) DEFAULT ” COMMENT ‘遠程訪問mysql服務器的客戶端ip地址’, `update_Before` VARCHAR(45) DEFAULT ” COMMENT ‘修改前的金額’, `update_After` VARCHAR(45) DEFAULT ” COMMENT ‘修改後的金額’, `gmt_create` TIMESTAMP NOT NULL DEFAULT ’0000-00-00 00:00:00′ COMMENT ‘創建時間’, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
我們可以使用觸發器記錄所有用戶對錶t_car,進行UPDATE操作修改數據的行爲進行記錄,觸發器語句體:
DELIMITER $$ CREATE TRIGGER tri_t_car BEFORE UPDATE ON t_car FOR EACH ROW BEGIN IF NEW.pay<>OLD.pay THEN INSERT INTO t_record(username,client_ip,update_Before,update_After,gmt_create) VALUES(SUBSTRING_INDEX(USER(),’@',1),SUBSTRING_INDEX(USER(),’@',-1),OLD.pay,NEW.pay,NOW()); END IF; END $$ DELIMITER ; |
測試:
測試用例的數據生成語句:
INSERT INTO t_car(car_number,card_number,pay) VALUES(SUBSTRING(RAND(),3,20),SUBSTRING(RAND(),3,10),SUBSTRING(RAND(),3,3)), (SUBSTRING(RAND(),3,20),SUBSTRING(RAND(),3,10),SUBSTRING(RAND(),3,3)), (SUBSTRING(RAND(),3,20),SUBSTRING(RAND(),3,10),SUBSTRING(RAND(),3,3)); root@localhost : test 11:14:49> SELECT * FROM t_car; +—-+—————–+————-+——-+ | ID | car_number | card_number | pay | +—-+—————–+————-+——-+ | 1 | 933606902075565 | 4065322181 | 231.0 | | 2 | 939605452064057 | 0025060456 | 193.0 | | 3 | 96105140723386 | 2241153588 | 237.0 | +—-+—————–+————-+——-+ 3 rows in set (0.00 sec) |
修改目標表數據的測試語句:
UPDATE t_car SET pay=100.5 WHERE ID=1;
查詢審計信息存儲的表:
root@localhost : test 11:15:51> SELECT * FROM t_record; +—-+———-+———–+—————+————–+———————+ | ID | username | client_ip | update_Before | update_After | gmt_create | +—-+———-+———–+—————+————–+———————+ | 1 | root | localhost | 231.0 | 100.5 | 2011-07-08 11:15:51 | +—-+———-+———–+—————+————–+———————+ 1 row in set (0.00 sec) |
可以看到需要審計的信息,都已經存儲到對應的審計表中,到此觸發器實現審計功能的需求就完整實現了。
總結
觸發器的用處非常多,關鍵是要結合業務場景使用,本文給大家介紹瞭如何使用觸發器實現數據庫的審計功能;我們還可以藉助觸發器實現2張表之間的數據同步問題,配合MEMORY引擎可以解決該存儲引擎缺陷:數據無法持久化,從而增強MEMORY引擎的使用場景;我們也可以利用觸發器實現異地,甚至跨國界多數據庫節點之間的數據同步業務需求,後續篇章給大家介紹,某著名電子商務公司利用觸發器,加應用程序實現多數據節點之間的數據同步問題。
原文出處:http://www.mysqlops.com/2011/07/08/mysql-trigger-audit.html