延續基礎篇,本文聚焦 DECLARE HANDLER 的優先級機制、DECLARE CONDITION 命名條件最佳實踐,以及生產級錯誤處理模式:統一錯誤碼、事務保護、日誌記錄、應用層協作。
當多個 HANDLER 匹配同一錯誤,MySQL 按 具體 > 泛化 順序選擇:
CREATE PROCEDURE sp_handler_priority() BEGIN DECLARE EXIT HANDLER FOR 1062 SELECT 'Level 1: MySQL 1062'; DECLARE EXIT HANDLER FOR SQLSTATE '23000' SELECT 'Level 2: SQLSTATE 23000'; DECLARE EXIT HANDLER FOR SQLEXCEPTION SELECT 'Level 3: SQLEXCEPTION'; INSERT INTO t_pk (id) VALUES (1); -- ok INSERT INTO t_pk (id) VALUES (1); -- 1062 END; -- 輸出: Level 1: MySQL 1062
-- 聲明 (必須在 HANDLER/CURSOR 之前) DECLARE duplicate_key CONDITION FOR 1062; DECLARE fk_violation CONDITION FOR 1216; DECLARE data_truncated CONDITION FOR SQLSTATE '22001'; -- 使用 DECLARE EXIT HANDLER FOR duplicate_key BEGIN SET @ret_code = -1001; SET @ret_msg = '唯一約束衝突'; END; DECLARE EXIT HANDLER FOR fk_violation SET @ret_code = -1002;
DELIMITER $$
CREATE PROCEDURE sp_biz_template(
IN p_param INT,
OUT p_code INT,
OUT p_msg VARCHAR(200)
)
MODIFIES SQL DATA
BEGIN
-- 1. 業務自定義條件
DECLARE biz_rule_violation CONDITION FOR SQLSTATE '45000';
-- 2. 系統異常兜底 (最外層)
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
CALL sp_log_error('sp_biz_template', 'SQLEXCEPTION', @sqlstate, @msg);
SET p_code = -999;
SET p_msg = '系統內部錯誤,請聯繫管理員';
END;
-- 3. 具體錯誤碼處理 (優先級高於 SQLEXCEPTION)
DECLARE EXIT HANDLER FOR 1062 -- 重複鍵
BEGIN
ROLLBACK;
SET p_code = -1001;
SET p_msg = '數據已存在,請勿重複提交';
END;
DECLARE EXIT HANDLER FOR 1216 -- 外鍵違反
BEGIN
ROLLBACK;
SET p_code = -1002;
SET p_msg = '關聯數據不存在';
END;
-- 4. 業務規則違反 (自定義 SQLSTATE 45000)
DECLARE EXIT HANDLER FOR biz_rule_violation
BEGIN
ROLLBACK;
SET p_code = -2001;
SET p_msg = '業務規則校驗失敗';
END;
-- 5. 正常業務流程
START TRANSACTION;
-- 校驗邏輯
IF p_param < 0 THEN
SIGNAL biz_rule_violation SET MESSAGE_TEXT = '參數不能為負';
END IF;
-- 核心寫操作
INSERT INTO biz_table (col1) VALUES (p_param);
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;
SET p_code = 0;
SET p_msg = 'SUCCESS';
END$$
DELIMITER ;
CREATE TABLE proc_error_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, proc_name VARCHAR(64), error_type VARCHAR(32), -- 'MYSQL_ERRNO' | 'SQLSTATE' | 'CUSTOM' error_code VARCHAR(20), error_msg TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE PROCEDURE sp_log_error( IN p_proc VARCHAR(64), IN p_type VARCHAR(32), IN p_code VARCHAR(20), IN p_msg TEXT ) BEGIN INSERT INTO proc_error_log (proc_name, error_type, error_code, error_msg) VALUES (p_proc, p_type, p_code, p_msg); END;
| 資料庫返回 | 應用層處理 | HTTP 狀態碼 |
|---|---|---|
| p_code = 0 | 正常返回業務數據 | 200 OK |
| p_code = -1xxx (業務錯誤) | 解析 p_msg 直接回傳前端 | 400 Bad Request |
| p_code = -999 (系統錯誤) | 記錄完整堆疊,返回通用提示,觸發告警 | 500 Internal Server Error |
IF v_cnt = 0 THEN ...。將異常處理標準化為「感知→分類→記錄→轉換→回滾→上報」流水線,能讓資料庫層輸出確定性、可觀測、可治理的錯誤語義,大幅降低系統故障 MTTR。