首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 教學

Oracle PL/SQL 異常處理完整指南:預定義、自訂與 RAISE_APPLICATION_ERROR 實戰

     編輯:Oracle 教學

為什麼需要結構化異常處理?

PL/SQL 區塊執行過程中可能遇到:資料未找到(NO_DATA_FOUND)、唯一約束違規(DUP_VAL_ON_INDEX)、除零錯誤(ZERO_DIVIDE)等運行時錯誤。若無處理,區塊直接終止,呼叫端只收到通用錯誤碼,難以定位業務問題。結構化 EXCEPTION 區塊讓我們能:

  • 捕獲特定錯誤並給出業務友好提示
  • 記錄錯誤上下文(SQLCODE、SQLERRM)供事後分析
  • 實現優雅降級或重試邏輯

異常處理架構

DECLARE
  -- 宣告區
BEGIN
  -- 執行區:可能拋出異常的業務邏輯
EXCEPTION
  WHEN exception_name THEN  -- 捕獲特定異常
    -- 處理邏輯
  WHEN OTHERS THEN          -- 捕獲所有其他異常(放最後)
    -- 通用處理:記錄、重拋、轉換
END;

三種拋出自訂異常的方式

方式一:宣告自訂異常 + RAISE

DECLARE
  e_insufficient_balance EXCEPTION;  -- 宣告
BEGIN
  IF balance < amount THEN
    RAISE e_insufficient_balance;    -- 直接拋出
  END IF;
EXCEPTION
  WHEN e_insufficient_balance THEN
    DBMS_OUTPUT.PUT_LINE('餘額不足!');
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLCODE || '---' || SQLERRM);
END;

方式二:RAISE_APPLICATION_ERROR(推薦用於 API/存儲過程)

可自定義錯誤碼(-20000 ~ -20999)與訊息,呼叫端(Java、Python 等)可直接捕獲具體錯誤碼。

BEGIN
  IF balance < amount THEN
    RAISE_APPLICATION_ERROR(-20001, '帳戶餘額不足,無法轉帳');
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error Code = ' || TO_CHAR(SQLCODE));
    DBMS_OUTPUT.PUT_LINE('Error Message = ' || SQLERRM);
END;

輸出:

Error Code = -20001
Error Message = ORA-20001: 帳戶餘額不足,無法轉帳

方式三:結合 PRAGMA EXCEPTION_INIT 映射 Oracle 錯誤碼

DECLARE
  e_parent_key_not_found EXCEPTION;
  PRAGMA EXCEPTION_INIT(e_parent_key_not_found, -2291);  -- ORA-02291
BEGIN
  DELETE FROM dept WHERE deptno = 10;  -- 存在子記錄時觸發
EXCEPTION
  WHEN e_parent_key_not_found THEN
    DBMS_OUTPUT.PUT_LINE('無法刪除:存在關聯員工記錄');
END;

實戰範例:分頁查詢游標與異常處理

CREATE OR REPLACE PROCEDURE split_page_procedure(
  page_index IN NUMBER,
  page_size  IN NUMBER,
  key_word   IN VARCHAR2
) IS
  my_cursor   SYS_REFCURSOR;
  page_count  NUMBER(7);
  min_idx     NUMBER(7);
  max_idx     NUMBER(7);
  name_param  userlogin.user_name%TYPE;
  uid_param   userlogin.user_id%TYPE;
BEGIN
  -- 參數驗證
  IF page_index < 1 OR page_size < 1 THEN
    RAISE NO_DATA_FOUND;  -- 復用預定義異常
  END IF;

  min_idx := (page_index - 1) * page_size;
  max_idx := page_index * page_size;

  OPEN my_cursor FOR
    SELECT user_name, user_id
    FROM (SELECT u.*, ROWNUM rn FROM userlogin u WHERE ROWNUM < max_idx)
    WHERE rn > min_idx;

  LOOP
    FETCH my_cursor INTO name_param, uid_param;
    EXIT WHEN my_cursor%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('參數:name:' || name_param || ' uid:' || uid_param);
  END LOOP;
  CLOSE my_cursor;

  SELECT COUNT(1) INTO page_count FROM userlogin;
  DBMS_OUTPUT.PUT_LINE('返回的總記錄條數:' || page_count);

EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('分頁參數不合理 page_index:' || page_index || ' page_size:' || page_size);
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLCODE || '---' || SQLERRM);
END;

塊操作與異常處理最小範本

DECLARE
  v_param NUMBER(7,2) := 12;
BEGIN
  IF v_param > 10 THEN
    DBMS_OUTPUT.PUT_LINE('aaa');
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END;

關鍵最佳實踐

  1. 具體優於通用:優先捕獲具體異常(NO_DATA_FOUND、DUP_VAL_ON_INDEX),WHEN OTHERS 僅作最後防線。
  2. 錯誤碼區間:自訂錯誤碼限定在 -20000 ~ -20999,避免與 Oracle 保留碼衝突。
  3. 記錄上下文:WHEN OTHERS 區塊務必記錄 SQLCODE、SQLERRM、關鍵參數值,必要時寫入日誌表。
  4. 避免吞沒異常:WHEN OTHERS THEN NULL 是大忌,除非極少數刻意忽略場景(如清理臨時表)。
  5. 重拋機制:處理完業務邏輯後,可用 RAISE 重拋原異常或 RAISE_APPLICATION_ERROR 轉換為業務錯誤碼,讓上層統一處理。

異常處理不是「捕獲錯誤讓程式不掛掉」,而是「在錯誤發生時,保留足夠線索讓問題可追蹤、可定位、可恢復」。好的異常設計,是系統穩健度的隱形基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved