HANDLER 語法結構
DECLARE {CONTINUE | EXIT} HANDLER FOR condition_value
statement;
condition_value:
SQLSTATE [VALUE] sqlstate_value
| condition_name
| SQLWARNING -- 01xxx
| NOT FOUND -- 02xxx
| SQLEXCEPTION -- 其他
| mysql_error_code
| 類型 | 行為 | 適用場景 |
EXIT | 發生條件 → **跳出當前 BEGIN...END 區塊** | 嚴重錯誤,需回滾、停止後續邏輯 |
CONTINUE | 發生條件 → **繼續執行下一條語句** | 警告、非致命錯誤、記錄日誌後繼續 |
完整範例:品牌產品鍵值合併任務
DELIMITER $$
CREATE PROCEDURE `Merge_BrandProductKey`()
BEGIN
-- 異常處理器:記錄錯誤、提交當前事務、退出過程
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'exception in MergeBrandProductKey', CONCAT(@@error_count, ' errors'));
COMMIT;
END;
-- 警告處理器:記錄警告、繼續執行
DECLARE CONTINUE HANDLER FOR SQLWARNING
BEGIN
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'warnings in MergeBrandProductKey', CONCAT(@@warning_count, ' warnings'));
COMMIT;
END;
-- 任務開始日誌
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'start in MergeBrandProductKey', '');
COMMIT;
-- ===== 核心業務邏輯(多條 UPDATE,各自獨立事務) =====
-- 規則 1:通用匹配
UPDATE `brandproductkey` bpk
JOIN (
SELECT bp.brandproductid, bp.brandproductenname, bp.brandid
FROM brandproduct bp
JOIN (SELECT brandid, brandproductid FROM brandproductdriverrelation GROUP BY brandid, brandproductid) bpr
ON bp.brandid = bpr.brandid AND bp.brandproductid = bpr.brandproductid
) bpp ON bpk.brandid = bpp.brandid
AND UPPER(bpk.brandproductkeyname) = UPPER(REPLACE(bpp.brandproductenname, ' ', ''))
SET bpk.brandproductid = bpp.brandproductid
WHERE bpk.brandproductid = 0 AND bpk.brandid = bpp.brandid;
COMMIT;
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'rule normal', ''); COMMIT;
-- 規則 2:SONY 系列 / 變體匹配
UPDATE `brandproductkey` bpk
JOIN (
SELECT bp.brandproductid, bp.brandproductenname, bp.brandid
FROM brandproduct bp
JOIN (SELECT brandid, brandproductid FROM brandproductdriverrelation GROUP BY brandid, brandproductid) bpr
ON bp.brandid = bpr.brandid AND bp.brandproductid = bpr.brandproductid AND bp.brandid = 60
) bpp ON bpk.brandid = bpp.brandid
AND bpp.brandproductenname LIKE CONCAT(bpk.brandproductkeyname, '/%')
SET bpk.brandproductid = bpp.brandproductid
WHERE bpk.brandproductid = 0 AND bpk.brandid = bpp.brandid;
COMMIT;
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'rule sony 1', ''); COMMIT;
-- ... 更多規則 ...
-- 任務結束日誌
INSERT INTO `t_runninglog` VALUES (DEFAULT, DEFAULT, 'finish in MergeBrandProductKey', '');
COMMIT;
END$$
DELIMITER ;
關鍵設計點解析
- 每條核心 UPDATE 獨立事務:
COMMIT 後再寫日誌,避免單一大事務長鎖定、回滾成本高。
- EXIT HANDLER 針對 SQLEXCEPTION:捕獲死鎖、鍵衝突、資料截斷等嚴重錯誤,**記錄並提交已完成部分**,防止整個過程回滾導致前序規則白跑。
- CONTINUE HANDLER 針對 SQLWARNING:資料截斷、隱式轉換等警告**不中斷流程**,僅記錄供事後審計。
- 日誌表結構建議:
CREATE TABLE `t_runninglog` (
`id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`stage` VARCHAR(100) NOT NULL,
`detail` TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
- 優先級規則:MySQL 錯誤碼 > SQLSTATE > 命名條件。同一條件只能有一個 HANDLER 生效(按聲明順序)。
測試驗證
-- 故意製造錯誤:更新不存在的表
CALL Merge_BrandProductKey();
SELECT * FROM t_runninglog ORDER BY id DESC LIMIT 5;
進階:自定義條件名稱
DECLARE duplicate_key CONDITION FOR SQLSTATE '23000';
DECLARE EXIT HANDLER FOR duplicate_key
BEGIN
INSERT INTO t_runninglog(stage, detail) VALUES ('duplicate_key', '主鍵/唯一鍵衝突');
COMMIT;
END;