MySQL 提供靈活的 DECLARE HANDLER 機制,支援針對具體錯誤碼、SQLSTATE、條件類別 (SQLWARNING/NOT FOUND/SQLEXCEPTION) 甚至自定義命名條件進行捕獲。掌握其優先級規則與最佳實踐,能構建健壯的資料庫層錯誤處理體系。
DECLARE {CONTINUE | EXIT | UNDO} HANDLER FOR condition_value[,...] statement;
-- condition_value 六大來源:
-- 1. MySQL 錯誤碼 (數字,如 1062)
-- 2. SQLSTATE 'HY000' (標準 5 字元)
-- 3. 命名條件 (DECLARE ... CONDITION FOR ...)
-- 4. SQLWARNING (所有 01 開頭 SQLSTATE)
-- 5. NOT FOUND (所有 02 開頭 SQLSTATE,游標/SELECT INTO 無數據)
-- 6. SQLEXCEPTION (除 00/01/02 外所有 SQLSTATE,即真正的錯誤)
| 動作 | 行為 | 默認場景 |
|---|---|---|
| CONTINUE | 執行 statement 後繼續執行後續語句 | SQLWARNING, NOT FOUND |
| EXIT | 執行 statement 後退出當前 BEGIN...END 區塊 | SQLEXCEPTION |
| UNDO | 回滾前面操作 (MySQL 暫不支援) | — |
當多個 HANDLER 同時匹配同一錯誤時,MySQL 按照 具體程度 選擇最優先的:
CREATE PROCEDURE sp_priority_demo() BEGIN -- 1. 最優先:錯誤碼 1062 DECLARE EXIT HANDLER FOR 1062 SELECT 'Handler 1: MySQL 1062'; -- 2. 次優先:SQLSTATE 23000 (含 1062) DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'Handler 2: SQLSTATE 23000'; -- 3. 最低:SQLEXCEPTION (含 23000) DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'Handler 3: SQLEXCEPTION'; INSERT INTO article_tags (article_id, tag_id) VALUES (1, 1); -- 首次成功 INSERT INTO article_tags (article_id, tag_id) VALUES (1, 1); -- 觸發 1062 END; -- 執行結果:輸出 "Handler 1: MySQL 1062"
-- 聲明 DECLARE duplicate_key CONDITION FOR 1062; DECLARE fk_violation CONDITION FOR 1216; DECLARE data_too_long CONDITION FOR SQLSTATE '22001'; -- 使用 DECLARE EXIT HANDLER FOR duplicate_key BEGIN SET @ret = -1; SET @msg = '唯一鍵衝突'; END; DECLARE EXIT HANDLER FOR fk_violation SET @ret = -2;
CREATE TABLE article_tags (
article_id INT,
tag_id INT,
PRIMARY KEY (article_id, tag_id)
) ENGINE=InnoDB;
DELIMITER $$
CREATE PROCEDURE sp_add_article_tag(
IN p_art_id INT,
IN p_tag_id INT
)
BEGIN
DECLARE EXIT HANDLER FOR 1062
SELECT CONCAT('重複鍵: (', p_art_id, ',', p_tag_id, ')') AS msg;
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT '系統異常,操作已回滾' AS msg;
END;
START TRANSACTION;
INSERT INTO article_tags (article_id, tag_id) VALUES (p_art_id, p_tag_id);
COMMIT;
SELECT COUNT(*) AS tag_cnt FROM article_tags WHERE article_id = p_art_id;
END$$
DELIMITER ;
ROLLBACK,防止部分提交造成數據不一致。錯誤處理不是「寫上 HANDLER 就完事」,而是要構建「感知→記錄→轉換→回滾→上報」的完整鏈路,讓資料庫層成為系統穩定性的最後一道防線。