異常處理
程序在執行過程中有可能出錯。運行時錯誤叫做異常
默認情況下,當過程運行出錯時,過程會立即終止,並打印系統錯誤消息
沒有異常處理的存儲過程,執行過程中非常難以預測執行結果,所以儘量存儲過程中加上異常處理部分。
注意:
1、異常後面的第一條SQL語句(也可以是begin end)與異常是一起的,他們一起執行。
2、一開始執行語句時不執行異常處理部分,執行到報錯的地方時,跳到異常處理的地方,如果能捕獲就執行異常處理部分,不能捕獲就不執行異常處理部分。執行完異常處理部分有兩個選擇continue、exit。
案例
1、
DELIMITER $$
CREATE PROCEDURE duplicate_teams(
OUT p_processed SMALLINT)
BEGIN
SET p_processed = 1;
INSERT INTO TEAMS VALUES(2,27,'third');
SET p_processed = 2;
END$$
DELIMITER ;
mysql> CALL duplicate_teams(@processed);
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'
2、
DELIMITER $$
CREATE PROCEDURE small_mistake1(
OUT error VARCHAR(5))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
SET error = '23000';
select error;
SET error = '00000';
select error;
INSERT INTO TEAMS VALUES(2,27,'third');
SET error = '23001';
END$$
DELIMITER ;
mysql> call small_mistake1(@a);
+-------+
| error |
+-------+
| NULL |
+-------+
| error |
+-------+
| 00000 |
mysql> select @a;
+-------+
| @a |
+-------+
| 23001 |
異常處理中的SQL語句,一般用來記錄錯誤發生時的一些信息。
3、
delimiter $$
CREATE PROCEDURE handlerdemo ()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
SET @x = 1;
INSERT INTO test.t VALUES (1);
SET @x = 2;
INSERT INTO test.t VALUES (1);
SET @x = 3;
END$$
delimiter ;
CALL handlerdemo()
mysql> select @x2;
+------+
| @x2 |
+------+
| 1 |
mysql> select @x;
+------+
| @x |
+------+
| 3 |
4、
DELIMITER $$
CREATE PROCEDURE small_mistake2(
OUT error VARCHAR(5))
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '23000'
SET error = '23000';
select error;
SET error = '00000';
select error;
INSERT INTO TEAMS VALUES(2,27,'third');
SET error = '23001';
END$$
DELIMITER ;
mysql> call small_mistake2(@a);
| error |
+-------+
| NULL |
+-------+
| error |
+-------+
| 00000 |
mysql> select @a;
+-------+
| @a |
+-------+
| 23000 |
例15:創建過程,插入一個重複的2號球隊
DELIMITER $$
CREATE PROCEDURE duplicate_teams(
OUT p_processed SMALLINT)
BEGIN
SET p_processed = 1;
INSERT INTO teams VALUES(2,27,'third');
SET p_processed = 2;
END$$
DELIMITER ;
客戶端調用:
CALL duplicate_teams(@processed);
異常處理好處:
1.出錯不報錯
2.可以進行處理
處理往往是記錄出錯時的一些信息
異常處理案例:
1、
CREATE TABLE test2(
id INT PRIMARY KEY,
NAME VARCHAR(100)
)ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
SELECT * FROM test2;
INSERT INTO test2 VALUES (1,'f');
DROP PROCEDURE IF EXISTS proc_exception1;
DELIMITER //
CREATE PROCEDURE proc_exception1(OUT error VARCHAR(10))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING,NOT FOUND,SQLEXCEPTION
SET error = '23000';
#變量賦值
SET error = '00000';
#執行sql
INSERT INTO test2 VALUES (1,'f');
#變量賦值
SET error = '23001';
END //
DELIMITER ;
CALL proc_exception1(@error);
SELECT @error;
DECLARE … HANDLER語句
DECLARE handler_action HANDLER
FOR condition_value [, condition_value] ...
Statement
其中:
handler_action:
CONTINUE | EXIT
condition_value:
mysql_error_code |
SQLSTATE [VALUE] sqlstate_value |
condition_name |
SQLWARNING |
NOT FOUND |
SQLEXCEPTION
該語句定義了一個異常處理程序,指定了當某條語句出錯時,採取什麼操作
語義:當某個錯誤( condition_value )發生時,就執行指定的語句(Statement),執行完之後,再決定如何操作( handler_action )
CONTINUE: 繼續執行當前的程序
EXIT: 當前程序終止
該語句必須出現在變量或條件聲明的後面
Statement
可以是單條語句或複合語句
關於SQLSTATE碼
每個MySQL錯誤都有一個唯一的數字錯誤編號( mysql_error_code ),每個錯誤又對應一個5字符的SQLSTATE碼( ANSI SQL 採用)
例16:創建過程,插入一個重複的2號球隊。處理異常,在輸出參數中包含出錯消息的SQLSTATE碼
DELIMITER $$
CREATE PROCEDURE small_mistake1(
OUT error VARCHAR(5))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
SET error = '23000';
SET error = '00000';
-- 該語句會出錯
INSERT INTO teams VALUES(2,27,'third');
-- 該語句在異常處理結束後會繼續得到執行
SET error = '23001';
END$$
DELIMITER ;
CALL small_mistake1(@error);
SELECT @error;
DELIMITER $$
CREATE PROCEDURE small_mistake2(
OUT error VARCHAR(5))
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '23000'
SET error = '23000';
SET error = '00000';
-- 該語句會出錯
INSERT INTO teams VALUES(2,27,'third');
-- 該語句在異常處理結束後不會得到執行
SET error = '23001';
END$$
DELIMITER ;
CALL small_mistake2(@error);
SELECT @error;
可以在一個過程中定義幾個異常處理程序,針對不同的錯誤進行不同的處理
例17:
DELIMITER $$
CREATE PROCEDURE small_mistake3(
OUT error VARCHAR(5))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
SET error = '23000';
DECLARE CONTINUE HANDLER FOR SQLSTATE '21S01'
SET error = '21S01';
-- 該語句會出錯.多了一個參數
INSERT INTO teams VALUES(2,27,'third',5);
END$$
DELIMITER ;
CALL small_mistake3(@error);
SELECT @error;
上例中的異常處理程序也可以使用錯誤編號。例如:
DECLARE CONTINUE HANDLER FOR 1062
SET error = '23000';
DECLARE CONTINUE HANDLER FOR 1136
SET error = '21S01';
以’01’開頭的所有sqlstate碼都對應SQLWARNING處理程序;
以’02’開頭的所有sqlstate碼都對應NOT FOUND 處理程序;
那些不以’01’或’02’開頭的所有sqlstate碼都對應SQLEXCEPTION處理程序。
當我們不想爲每個錯誤都定義一個處理程序時,可以使用這3個處理程序
例18:
DELIMITER $$
CREATE PROCEDURE small_mistake4(
OUT error VARCHAR(5))
BEGIN
DECLARE CONTINUE HANDLER FOR SQLWARNING,NOT FOUND,SQLEXCEPTION
SET error = 'xxxxx';
-- 該語句會出錯.
INSERT INTO teams VALUES(2,27,'third');
END$$
DELIMITER ;
CALL small_mistake4(@error);
SELECT @error;
給異常編碼起名字
DECLARE condition_name CONDITION FOR condition_value
其中,condition_value:
mysql_error_code |
SQLSTATE [VALUE] sqlstate_value
爲了提高可讀性,可以給某個sqlstate代碼或mysql錯誤代碼一個名字,並且在後面的異常處理程序中使用這個名字
例19:
DELIMITER $$
CREATE PROCEDURE small_mistake5(
OUT error VARCHAR(5))
BEGIN
DECLARE non_unique CONDITION FOR SQLSTATE '23000';
DECLARE CONTINUE HANDLER FOR non_unique
SET error = '23000';
-- 該語句會出錯.
INSERT INTO teams VALUES(2,27,'third');
END$$
DELIMITER ;
CALL small_mistake5(@error);
SELECT @error;
案例:
DROP PROCEDURE IF EXISTS proc_exception2;
DELIMITER //
CREATE PROCEDURE proc_exception2(OUT error VARCHAR(10))
BEGIN
DECLARE non_unique CONDITION FOR 1062;
DECLARE CONTINUE HANDLER FOR non_unique -- for 1062
SET error = '23000';
#變量賦值
SET error = '00000';
#執行sql
INSERT INTO test2 VALUES (1,'f');
#變量賦值
SET error = '23001';
END //
DELIMITER ;
CALL proc_exception2(@error);
SELECT @error;
注意:
1.可以在捕獲錯誤後不進行操作,即忽略錯誤,如:DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;即將異常處理的SQL語句做成空的。
2.異常捕獲只能定義在begin下面;
3.當異常處理被定義在多個begin end嵌套中間時,報錯時,先去最近的begin end中的異常處理中去捕獲,如果能捕獲到就不會去更外層的begin end 去捕獲了;
4.當有多層begin end的時候,每層都有自己完善的異常處理,自己異常,自己這層去處理。
異常處理嵌套問題
案例:
DELIMITER $$
CREATE PROCEDURE small_mistake6(
OUT error VARCHAR(5))
BEGIN
DECLARE non_unique CONTINUE FOR SQLSTATE '23000';
DECLARE CONTINUE HANDLER FOR non_unique
BEGIN
SET error=’23000’;
select error;
END;
INSERT INTO TEAMS VALUES(2,27,’third’);
END$$
DELIMITER ;
異常傳播
在嵌套塊的情況下,內部塊中發生異常了,首先由本塊的異常處理程序來處理,如果本塊沒有處理,則由外部塊的異常處理程序來處理
例20:
DELIMITER $$
CREATE PROCEDURE small_mistake6()
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '23000'
SET @processed = 100;
BEGIN
DECLARE CONTINUE HANDLER FOR SQLSTATE '21000'
SET @processed = 200;
-- 該語句會出錯.
INSERT INTO teams VALUES(2,27,'third');
END;
END$$
DELIMITER ;
CALL small_mistake6();
SELECT @processed;