| 異常名稱 | 錯誤碼 | 觸發情境 |
| NO_DATA_FOUND | +100 / -1403 | SELECT INTO 0 行 |
| TOO_MANY_ROWS | -1422 | SELECT INTO >1 行 |
| ZERO_DIVIDE | -1476 | 除以 0 |
| DUP_VAL_ON_INDEX | -1 | 唯一鍵衝突 |
| VALUE_ERROR | -6502 | 字串截斷/型別轉換失敗 |
| INVALID_NUMBER | -1722 | SQL 中字串轉數字失敗 |
| CURSOR_ALREADY_OPEN | -6511 | 重複 OPEN 游標 |
三種觸發異常方式
- Oracle 自動拋出: 違反約束、型別錯誤等。
- RAISE 手動拋出:
RAISE e_my_exc; 或 RAISE NO_DATA_FOUND;
- RAISE_APPLICATION_ERROR: 攜帶自訂錯誤訊息(-20000 ~ -20999)。
RAISE_APPLICATION_ERROR(-20001, '餘額不足,無法扣款');
異常傳遞機制
巢狀區塊:子區塊未處理的異常**自動向外傳遞**至父區塊的 EXCEPTION 區;父區塊亦無處理則繼續向外,直到最外層或無人處理則回報給呼叫端(Client/JDBC/OCI)。
BEGIN
BEGIN
v := 1/0; -- 子區塊拋 ZERO_DIVIDE
EXCEPTION WHEN OTHERS THEN NULL; -- 子區塊吞掉,不傳遞
END;
EXCEPTION WHEN ZERO_DIVIDE THEN ... -- 父區塊捕捉不到,因子區塊已處理(雖為 NULL)
END;
**最佳實踐:子區塊僅處理能真正恢復的異常,其餘拋給父區塊統一記錄/轉換。**
實戰範例:業務異常統一轉碼
DECLARE
e_insuff_fund EXCEPTION;
PRAGMA EXCEPTION_INIT(e_insuff_fund, -20001);
BEGIN
IF balance < amount THEN
RAISE_APPLICATION_ERROR(-20001, '帳戶 '||acc_id||' 餘額 '||balance||' 不足扣除 '||amount);
END IF;
-- 扣款邏輯
EXCEPTION
WHEN e_insuff_fund THEN
LOG_ERROR(SQLCODE, SQLERRM);
RAISE; -- 重新拋出讓上層決定回滾/重試
WHEN OTHERS THEN
LOG_ERROR(SQLCODE, SQLERRM);
RAISE_APPLICATION_ERROR(-20999, '系統錯誤:'||SQLERRM);
END;
總結
異常處理 = 程式健壯性的最後防線。核心口訣:**預定義異常直接 WHEN、自定義異常 PRAGMA 綁錯誤碼、RAISE_APPLICATION_ERROR 傳遞業務語意、子區塊不吞無法恢復的異常、WHEN OTHERS 最後兜底並記錄 SQLCODE/SQLERRM**。
- 上一篇文章: Oracle 觸發器完整指南:分類、編寫、:NEW/:OLD、複合觸發器與管理實務
- 下一篇文章: Oracle 游標完整教學:隱式/顯式游標、FOR 迴圈、參數化游標、游標變數與 BULK COLLECT
Copyright ©
程式碼之家 All Rights Reserved