PL/SQL 區塊執行過程中可能遇到:資料未找到(NO_DATA_FOUND)、唯一約束違規(DUP_VAL_ON_INDEX)、除零錯誤(ZERO_DIVIDE)等運行時錯誤。若無處理,區塊直接終止,呼叫端只收到通用錯誤碼,難以定位業務問題。結構化 EXCEPTION 區塊讓我們能:
DECLARE
-- 宣告區
BEGIN
-- 執行區:可能拋出異常的業務邏輯
EXCEPTION
WHEN exception_name THEN -- 捕獲特定異常
-- 處理邏輯
WHEN OTHERS THEN -- 捕獲所有其他異常(放最後)
-- 通用處理:記錄、重拋、轉換
END;
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;
可自定義錯誤碼(-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: 帳戶餘額不足,無法轉帳
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;
NO_DATA_FOUND、DUP_VAL_ON_INDEX),WHEN OTHERS 僅作最後防線。-20000 ~ -20999,避免與 Oracle 保留碼衝突。WHEN OTHERS 區塊務必記錄 SQLCODE、SQLERRM、關鍵參數值,必要時寫入日誌表。WHEN OTHERS THEN NULL 是大忌,除非極少數刻意忽略場景(如清理臨時表)。RAISE 重拋原異常或 RAISE_APPLICATION_ERROR 轉換為業務錯誤碼,讓上層統一處理。異常處理不是「捕獲錯誤讓程式不掛掉」,而是「在錯誤發生時,保留足夠線索讓問題可追蹤、可定位、可恢復」。好的異常設計,是系統穩健度的隱形基石。