Why Triggers 為什么要用觸發(fā)器
我們在MySQL 5.0中包含對觸發(fā)器的支持是由于以下原因:
MySQL早期版本的用戶(hù)長(cháng)期有需要觸發(fā)器的要求。
我們曾經(jīng)許諾支持所有ANSI標準的特性。
您可以使用它來(lái)檢查或預防壞的數據進(jìn)入數據庫。
您可以改變或者取消INSERT, UPDATE以及DELETE語(yǔ)句。
您可以在一個(gè)會(huì )話(huà)中監視數據改變的動(dòng)作。
在這里我假定大家都讀過(guò)"MySQL新特性"叢書(shū)的第一集--"MySQL存儲過(guò)程",那么大家都應該知道MySQL至此存儲過(guò)程和函數,那是很重要的知識,因為在觸發(fā)器中你可以使用在函數中使用的語(yǔ)句。特別舉個(gè)例子:
復合語(yǔ)句(BEGIN / END)是合法的.
流控制(Flow-of-control)語(yǔ)句(IF, CASE, WHILE, LOOP, WHILE, REPEAT, LEAVE,ITERATE)也是合法的.
變量聲明(DECLARE)以及指派(SET)是合法的.
允許條件聲明.
異常處理聲明也是允許的.
但是在這里要記住函數有受限條件:不能在函數中訪(fǎng)問(wèn)表.因此在函數中使用以下語(yǔ)句是非法的。
ALTER 'CACHE INDEX' CALL COMMIT CREATE DELETE
DROP 'FLUSH PRIVILEGES' GRANT INSERT KILL
LOCK OPTIMIZE REPAIR REPLACE REVOKE
ROLLBACK SAVEPOINT 'SELECT FROM table'
'SET system variable' 'SET TRANSACTION'
SHOW 'START TRANSACTION' TRUNCATE UPDATE
在觸發(fā)器中也有完全一樣的限制.
觸發(fā)器相對而言比較新,因此會(huì )有(bugs)缺陷.所以我在這里給大家警告,就像我在存儲過(guò)程書(shū)中所說(shuō)那樣.不要在含有重要數據的數據庫中使用這個(gè)觸發(fā)器,如果需要的話(huà)在一些以測試為目的的數據庫上使用,同時(shí)在你對表創(chuàng )建觸發(fā)器時(shí)確認這些數據庫是默認的。
Syntax 語(yǔ)法
1. Syntax: Name 語(yǔ)法:命名規則
CREATE TRIGGER <觸發(fā)器名稱(chēng)> <--
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON <表名稱(chēng)>
FOR EACH ROW
<觸發(fā)器SQL語(yǔ)句>
觸發(fā)器必須有名字,最多64個(gè)字符,可能后面會(huì )附有分隔符.它和MySQL中其他對象的命名方式基本相象.
這里我有個(gè)習慣:就是用表的名字+'_'+觸發(fā)器類(lèi)型的縮寫(xiě).因此如果是表t26,觸發(fā)器是在事件UPDATE(參考下面的點(diǎn)(2)和(3))之前(BEFORE)的,那么它的名字就是t26_bu。
2. Syntax: Time 語(yǔ)法:觸發(fā)時(shí)間
CREATE TRIGGER <觸發(fā)器名稱(chēng)>
{ BEFORE | AFTER } <--
{ INSERT | UPDATE | DELETE }
ON <表名稱(chēng)>
FOR EACH ROW
<觸發(fā)的SQL語(yǔ)句>
觸發(fā)器有執行的時(shí)間設置:可以設置為事件發(fā)生前或后。
3. Syntax: Event語(yǔ)法:事件
CREATE TRIGGER <觸發(fā)器名稱(chēng)>
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE } <--
ON <表名稱(chēng)>
FOR EACH ROW
<觸發(fā)的SQL語(yǔ)句>
同樣也能設定觸發(fā)的事件:它們可以在執行insert、update或delete的過(guò)程中觸發(fā)。
4. Syntax: Table 語(yǔ)法:表
CREATE TRIGGER <觸發(fā)器名稱(chēng)>
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON <表名稱(chēng)> <--
FOR EACH ROW
<觸發(fā)的SQL語(yǔ)句>
觸發(fā)器是屬于某一個(gè)表的:當在這個(gè)表上執行插入、更新或刪除操作的時(shí)候就導致觸發(fā)器的激活.
我們不能給同一張表的同一個(gè)事件安排兩個(gè)觸發(fā)器。
5. Syntax: Granularity 語(yǔ)法:( :( 步長(cháng))觸發(fā)間隔
CREATE TRIGGER <觸發(fā)器名稱(chēng)>
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON <表名稱(chēng)>
FOR EACH ROW <--
<觸發(fā)的SQL語(yǔ)句>
觸發(fā)器的執行間隔:FOR EACH ROW子句通知觸發(fā)器每隔一行執行一次動(dòng)作,而不是對整個(gè)表執行一次。
6. Syntax: Statement 語(yǔ)法:語(yǔ)句
CREATE TRIGGER <觸發(fā)器名稱(chēng)>
{ BEFORE | AFTER }
{ INSERT | UPDATE | DELETE }
ON <表名稱(chēng)>
FOR EACH ROW
<觸發(fā)的SQL語(yǔ)句> <--
觸發(fā)器包含所要觸發(fā)的SQL語(yǔ)句:這里的語(yǔ)句可以是任何合法的語(yǔ)句,包括復合語(yǔ)句,但是這里的語(yǔ)句受的限制和函數的一樣。
Privileges權限
你必須擁有相當大的權限才能創(chuàng )建觸發(fā)器(CREATE TRIGGER)。如果你已經(jīng)是Root用戶(hù),那么就足夠了。這跟SQL的標準有所不同,我也希望能盡快改成標準的。
因此在下一個(gè)版本的MySQL中,你完全有可能看到有一種叫做CREATE TRIGGER的新權限。然后通過(guò)這樣的方法賦予:
GRANT CREATE TRIGGER ON <表名稱(chēng)> TO <用戶(hù)或用戶(hù)列表>;
也可以通過(guò)這樣收回權限:
REVOKE CREATE TRIGGER ON <表名稱(chēng)> FROM <用戶(hù)或用戶(hù)列表>;
Referring to OLD and NEW columns 關(guān)于舊的和新創(chuàng )建的列的標識
在觸發(fā)器的SQL語(yǔ)句中,你可以關(guān)聯(lián)表中的任意列。但你不能僅使用列的名稱(chēng)去標識,那會(huì )使系統混淆,因為那里可能會(huì )有列的新名(這可能正是你要修改的,你的動(dòng)作可能正是要修改列名),還有列的舊名存在。因此你必須用這樣的語(yǔ)法來(lái)標識:
"NEW . column_name"或者"OLD . column_name".這樣在技術(shù)上處理(NEW | OLD . column_name)新和舊的列名屬于創(chuàng )建了過(guò)渡變量("transition variables")。
對于INSERT語(yǔ)句,只有NEW是合法的;對于DELETE語(yǔ)句,只有OLD才合法;而UPDATE語(yǔ)句可以在和NEW以及OLD同時(shí)使用。下面是一個(gè)UPDATE中同時(shí)使用NEW和OLD的例子。
CREATE TRIGGER t21_au
BEFORE UPDATE ON t22
FOR EACH ROW
BEGIN
SET @old = OLD . s1;
SET @new = NEW.s1;
END;//
現在如果t21表中的s1列的值是55,那么執行了"UPDATE t21 SET s1 = s1 + 1"之后@old的值會(huì )變成55,而@new的值將會(huì )變成56。
Example of CREATE and INSERT CREATE和INSERT的例子
CREATE table with trigger創(chuàng )建有觸發(fā)器的表
這里所有的例程中我都假定大家的分隔符已經(jīng)設置成//(DELIMITER //)。
CREATE TABLE t22 (s1 INTEGER)//
CREATE TRIGGER t22_bi
BEFORE INSERT ON t22
FOR EACH ROW
BEGIN
SET @x = 'Trigger was activated!';
SET NEW.s1 = 55;
END;//
在最開(kāi)始我創(chuàng )建了一個(gè)名字為t22的表,然后在表t22上創(chuàng )建了一個(gè)觸發(fā)器t22_bi,當我們要向表中的行插入時(shí),觸發(fā)器就會(huì )被激活,執行將s1列的值改為55的動(dòng)作。
INSERT on table w ith a trigger使用觸發(fā)器執行插入動(dòng)作
mysql> INSERT INTO t22 VALUES (1)//
讓我們看如果向表t2中插入一行數據觸發(fā)器對應的表會(huì )怎么樣?
這里的插入的動(dòng)作是很常見(jiàn)的,我們不需要觸發(fā)器的權限來(lái)執行它。甚至不需要知道是否有觸發(fā)器關(guān)聯(lián)。
mysql> SELECT @x, t22.* FROM t22//
+------------------------+------+
| @x | s1 |
+------------------------+------+
| Trigger was activated! | 55 |
+------------------------+------+
1 row in set (0.00 sec)
大家可以看到INSERT動(dòng)作之后的結果,和我們預期的一樣,x標記被改動(dòng)了,同時(shí)這里插入的數據不是我們開(kāi)始輸入的插入數據,而是觸發(fā)器自己的數據。
Example of a "check" constraint
"check"完整性約束例子
What's a "check" constraint 什么是"check"約束
在標準的SQL語(yǔ)言中,我們可以在(CREATE TABLE)創(chuàng )建表的過(guò)程中使用"CHECK (condition)",
例如:
CREATE TABLE t25
(s1 INT, s2 CHAR(5), PRIMARY KEY (s1),
CHECK (LEFT(s2,1)='A'))
ENGINE=INNODB;
這里CHECK的意思是"當s2列的最左邊的字符不是'A'時(shí),insert和update語(yǔ)句都會(huì )非法",MySQL的視圖不支持CHECK,我個(gè)人是很希望它能支持的。但如果你很需要在表中使用這樣的功能,我建議大家使用觸發(fā)器來(lái)實(shí)現。
CREATE TABLE t25
(s1 INT, s2 CHAR(5),
PRIMARY KEY (s1))
ENGINE=INNODB//
CREATE TRIGGER t25_bi
BEFORE INSERT ON t25
FOR EACH ROW
IF LEFT(NEW.s2,1)<>'A' THEN SET NEW.s1=0; END IF;//
CREATE TRIGGER t25_bu
BEFORE UPDATE ON t25
FOR EACH ROW
IF LEFT(NEW.s2,1)<>'A' THEN SET NEW.s1=0; END IF;//
我只需要使用BEFORE INSERT和BEFORE UPDATE語(yǔ)句就行了,刪除了觸發(fā)器不會(huì )對表有影響,同時(shí)AFTER的觸發(fā)器也不能修改NEW的過(guò)程變量(transition variables)。為了激活觸發(fā)器,我執行了向表中的行插入s1=0的數據,之后只要執行符合LEFT(s2,1) <> 'A'條件的動(dòng)作都會(huì )失?。?/font>
INSERT INTO t25 VALUES (0,'a') /* priming the pump */ //
INSERT INTO t25 VALUES (5,'b') /* gets error '23000' */ //
Don't Believe The Old MySQL Manual
該拋棄舊的MySQL的手冊了
我在這里警告大家不要相信過(guò)去的MySQL手冊中所說(shuō)的了。我們已經(jīng)去掉了關(guān)于觸發(fā)器的錯誤的語(yǔ)句,但是仍舊有很多舊版本的手冊在網(wǎng)上,舉個(gè)例子,這是一個(gè)德國的Url上的:
http://dev.mysql.com/doc/mysql/de/ANSI_diff_Triggers.html.
這個(gè)手冊上說(shuō)觸發(fā)器就是存儲過(guò)程,忘掉吧,你也已經(jīng)看見(jiàn)了,觸發(fā)器就是觸發(fā)器,而存儲過(guò)程還是存儲過(guò)程。
手冊上還說(shuō)觸發(fā)器可以從其他表上來(lái)刪除,或者是當你刪除一個(gè)事務(wù)的時(shí)候激發(fā),無(wú)論他說(shuō)的是什么意思,忘掉吧,MySQL不會(huì )去實(shí)現這些的。
最后關(guān)于說(shuō)使用觸發(fā)器會(huì )對查詢(xún)速度產(chǎn)生影響的說(shuō)法也是錯的,觸發(fā)器不會(huì )對查詢(xún)產(chǎn)生任何影響。
Bugs
?。ú缓玫臇|西就不翻譯了)
On December 14 2004, I did an "Advanced Search" in http://bugs.mysql.com for 'trigger' or
'triggers', I found that there were 17 active bugs as of that date. Of course they might disappear
before you read this, but just in case they haven't, I'll mention the important ones. If they're still
there, you'll have to work around them when you're trying triggers.
Bug#5859 DROP TABLE does not drop triggers.
?。▌h除表的時(shí)候沒(méi)有自動(dòng)刪除觸發(fā)器)
When you drop a table, dropping the table's triggers should be automatic.
Bug#5892 Triggers have the wrong namespace.
?。ㄓ|發(fā)器的命名空間有錯,你必須在前面加上表的名字才能刪除觸發(fā)器,下面是例子)
You have to say "DROP TRIGGER <table name> . <trigger name>".
The correct way is "DROP TRIGGER <trigger name>".
聯(lián)系客服