MySQL5的異常處理 1. Sample Problem: Log Of Failures 問題樣例:故障記錄 當INSERT失敗時,我希望能將其記錄在日志文件中我們用來展示出錯處理的問題樣例是很普通的。我希望得到錯誤的記錄。當INSERT失敗時,我想在另一個文件中記下這些錯誤的信息,例如出錯時間,出錯原因等。我對插入特別感興趣的原因是它將違反外鍵關聯的約束 2. Sample Problem: Log Of Failures (2) mysql> CREATE TABLE t2 (s1 INT, PRIMARY KEY (s1))engine=innodb; mysql> CREATE TABLE t3 (s1 INT, KEY (s1), FOREIGN KEY (s1) REFERENCES t2 (s1))engine=innodb;// mysql> INSERT INTO t3 VALUES (5); ... ERROR 1216 (23000): Cannot add or update a child row: a foreign key constraint fails(這裡顯示的是系統的出錯信息) 我開始要創建一個主鍵表,以及一個外鍵表。我們使用的是InnoDB,因此外鍵關聯檢查是打開的。然後當我向外鍵表中插入非主鍵表中的值時,動作將會失敗。當然這種條件下可以很快找到錯誤號1216。 3. Sample Problem: Log Of Failures mysql> CREATE TABLE error_log (error_message CHAR(80)); 下一步就是建立一個在做插入動作出錯時存儲錯誤的表。 4. Sample Problem: Log Of Errors DECLARE handler_action HANDLER FOR condition_value [, condition_value] ... statement handler_action: CONTINUE | EXIT | UNDO condition_value: mysql_error_code | SQLSTATE [VALUE] sqlstate_value | condition_name | SQLWARNING | NOT FOUND | SQLEXCEPTION CREATE PROCEDURE p22 (parameter1 INT) BEGIN DECLARE EXIT HANDLER FOR 1216 INSERT INTO error_log VALUES (CONCAT('Time: ',current_date,'. Foreign Key Reference Failure For Value = ',parameter1)); INSERT INTO t3 VALUES (parameter1); END; 上面就是我們的程序。這裡的第一個語句DECLARE EXIT HANDLER是用來處理異常的。意思是如果錯誤1215發生了,這個程序將會在錯誤記錄表中插入一行。EXIT意思是當動作成功提交後退出這個復合語句。 5. Sample Problem: Log Of Errors CALL p22 (5) 調用這個存儲過程會失敗,這很正常,因為5值並沒有在主鍵表中出現。但是沒有錯誤信息返回因為出錯處理已經包含在過程中了。t3表中沒有增加任何東西,但是error_log表中記錄下了一些信息,這就告訴我們INSERT into table t3動作失敗。 DECLARE HANDLER syntax 聲明異常處理的語法 DECLARE { EXIT | CONTINUE } HANDLER FOR { error-number | { SQLSTATE error-string } | condition } SQL statement 上面就是錯誤處理的用法,也就是一段當程序出錯後自動觸發的代碼。MySQL允許兩種處理器,一種是EXIT處理,我們剛才所用的就是這種。另一種就是我們將要演示的,CONTINUE處理,它跟EXIT處理類似,不同在於它執行後,原主程序仍然繼續運行,那麼這個復合語句就沒有出口了。 1. DECLARE CONTINUE HANDLER example CONTINUE處理例子 CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); SET @x = 3; END;// 這是MySQL參考手冊上的CONTINUE處理的例子,這個例子十分好,所以我把它拷貝到這裡。通過這個例子我們可以看出CONTINUE處理是如何工作的。 2. DECLARE CONTINUE HANDLER聲明CONTINUE異常處理 CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; <-- SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); SET @x = 3; END;// 這次我將為SQLSTATE值定義一個處理程序。還記得前面我們使用的MySQL錯誤代碼1216嗎?事實上這裡的23000SQLSTATE是更常用的,當外鍵約束出錯或主鍵約束出錯就被調用了。 3. DECLARE CONTINUE HANDLER CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; <-- INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); SET @x = 3; END;// 這個存儲過程的第一個執行的語句是"SET @x = 1"。 4. DECLARE CONTINUE HANDLER example CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); <-- SET @x = 3; END;// 運行後值1被插入到主鍵表中。 5. DECLARE CONTINUE HANDLER CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; <-- INSERT INTO t4 VALUES (1); SET @x = 3; END;// 然後@x的值變為2。 6. DECLARE CONTINUE HANDLER example CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); <-- SET @x = 3; END;// 然後程序嘗試再次往主鍵表中插入數值,但失敗了,因為主鍵有唯一性限制。 7. DECLARE CONTINUE HANDLER example CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; <-- SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); SET @x = 3; END;// 由於插入失敗,錯誤處理程序被觸發,開始進行錯誤處理。下一個執行的語句是錯誤處理的語句,@x2被設為2。 8. DECLARE CONTINUE HANDLER example CREATE TABLE t4 (s1 int,primary key(s1));// CREATE PROCEDURE p23 () BEGIN DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1; SET @x = 1; INSERT INTO t4 VALUES (1); SET @x = 2; INSERT INTO t4 VALUES (1); SET @x = 3; <-- END;// 到這裡並沒有結束,因為這是CONTINUE異常處理。所以執行返回到失敗的插入語句之後,繼續執行將@x設定為3動作。 9. DECLARE CONTINUE HANDLER example mysql> CALL p23()// Query OK, 0 rows affected (0.00 sec) mysql> SELECT @x, @x2// +------+------+ | @x | @x2 | +------+------+ | 3 | 1 | +------+------+ 1 row in set (0.00 sec) 運行過程後我們觀察@x的值,很確定的可以知道是3,觀察@x2的值,為1。從這裡可以判斷程序運行無誤,完全按照我們的思路進行。大家可以花點時間去調整錯誤處理器,讓檢查放在語句段的首部,而不是放在可能出現錯誤的地方,雖然那樣看起來程序很紊亂,跳來跳去的感覺。但是這樣的代碼很安全也很清楚。 1. DECLARE CONDITION CREATE PROCEDURE p24 () BEGIN DECLARE `Constraint Violation` CONDITION FOR SQLSTATE '23000'; DECLARE EXIT HANDLER FOR `Constraint Violation` ROLLBACK; START TRANSACTION; INSERT INTO t2 VALUES (1); INSERT INTO t2 VALUES (1); COMMIT; END; // 這是另外一個錯誤處理的例子,在前面的基礎上修改的。事實上你可給SQLSTATE或者錯誤代碼其他的名字,你就可以在處理中使用自己定義的名字了。下面看它是怎麼實現的:我把表t2定義為InnoDB表,所以對這個表的插入操作都會ROLLBACK(回滾),ROLLBACK(回滾事務)也是恰好會發生的。因為對主鍵插入兩個同樣的值會導致SQLSTATE 23000錯誤發生,這裡SQLSTATE 23000是約束錯誤。 2. DECLARE CONDITION聲明條件 CREATE PROCEDURE p24 () BEGIN DECLARE `Constraint Violation` CONDITION FOR SQLSTATE '23000'; DECLARE EXIT HANDLER FOR `Constraint Violation` ROLLBACK; START TRANSACTION; INSERT INTO t2 VALUES (1); INSERT INTO t2 VALUES (1); COMMIT; END; // 這個約束錯誤會導致ROLLBACK(回滾事務)和SQLSTATE 23000錯誤發生。 3. DECLARE CONDITION mysql> CALL p24()// Query OK, 0 rows affected (0.28 sec) mysql> SELECT * FROM t2// Empty set (0.00 sec) 我們調用這個存儲過程看結果是什麼,從上面結果我們看到表t2沒有插入任何記錄。全部事務都回滾了。這正是我們想要的。 4. DECLARE CONDITION mysql> CREATE PROCEDURE p9 () -> BEGIN -> DECLARE EXIT HANDLER FOR NOT FOUND BEGIN END; -> DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END; -> DECLARE EXIT HANDLER FOR SQLWARNING BEGIN END; -> END;// Query OK, 0 rows affected (0.00 sec) 這裡是三個預聲明的條件:NOT FOUND (找不到行), SQLEXCEPTION (錯誤),SQLWARNING (警告或注釋)。因為它們是預聲明的,因此不需要聲明條件就可以使用。不過如果你去做這樣的聲明:"DECLARE SQLEXCEPTION CONDITION ...",你將會得到錯誤信息提示。