首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫

MySQL 存儲過程異常處理實戰:DECLARE HANDLER 捕獲 SQLEXCEPTION/SQLWARNING 並記錄日誌

     編輯:MySQL 資料庫相關知識

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 ;

關鍵設計點解析

  1. 每條核心 UPDATE 獨立事務:COMMIT 後再寫日誌,避免單一大事務長鎖定、回滾成本高。
  2. EXIT HANDLER 針對 SQLEXCEPTION:捕獲死鎖、鍵衝突、資料截斷等嚴重錯誤,**記錄並提交已完成部分**,防止整個過程回滾導致前序規則白跑。
  3. CONTINUE HANDLER 針對 SQLWARNING:資料截斷、隱式轉換等警告**不中斷流程**,僅記錄供事後審計。
  4. 日誌表結構建議:
    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;
  5. 優先級規則: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;
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved