当前位置: 首页 > news >正文

PL/SQL:异常处理补充

PL/SQL异常处理补充:本文在基础异常处理语法基础上,重点补充了5个实用知识点:

  1. 常见预定义异常(如NO_DATA_FOUND)及其应用场景;
  2. 两种自定义异常实现方式(异常别名与RAISE_APPLICATION_ERROR);
  3. 异常处理最佳实践,包括日志表设计、带步骤标记的模板和异常处理策略决策树;
  4. 高级技巧如异常传播、批量操作异常隔离和11g+的堆栈追踪;
  5. 推荐的异常处理规范模板。

特别强调事务性操作必须ROLLBACK,并通过日志表记录异常细节,为开发人员提供了从基础到进阶的完整异常处理方案。


PL/SQL:异常处理补充


一、已有知识梳理(保持结构清晰)

1. 核心语法

sql

EXCEPTION WHEN 异常1 THEN 处理动作1; WHEN 异常2 THEN 处理动作2; WHEN OTHERS THEN 处理动作; -- 捕获所有未处理的异常

2. 关键内置函数

函数说明
SQLERRM返回完整的异常错误文本
SQLCODE返回错误代码(数字)

二、重要补充内容

补充1:常见预定义异常(不用声明直接用)

异常名触发场景SQLCODE
NO_DATA_FOUNDSELECT INTO 未查到数据+100
TOO_MANY_ROWSSELECT INTO 返回多行-1422
DUP_VAL_ON_INDEX违反唯一约束/主键-1
INVALID_CURSOR无效游标操作-1001
ZERO_DIVIDE除数为0-1476
INVALID_NUMBER字符串转数字失败-1722
LOGIN_DENIED登录失败-1017
PROGRAM_ERRORPL/SQL内部错误-6501

示例:按具体异常分类处理

sql

CREATE OR REPLACE PROCEDURE P_EXAMPLE(P_EMPNO NUMBER) IS V_ENAME VARCHAR2(100); BEGIN SELECT ENAME INTO V_ENAME FROM EMP WHERE EMPNO = P_EMPNO; DBMS_OUTPUT.PUT_LINE(V_ENAME); EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('错误:员工编号' || P_EMPNO || '不存在'); WHEN TOO_MANY_ROWS THEN DBMS_OUTPUT.PUT_LINE('错误:数据重复,查出多行'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('未知错误:' || SQLERRM); RAISE; -- 重新抛出异常 END;

补充2:自定义异常(业务逻辑异常)

方式一:预定义异常别名

sql

DECLARE E_SAL_TOO_HIGH EXCEPTION; PRAGMA EXCEPTION_INIT(E_SAL_TOO_HIGH, -20001); -- 绑定自定义错误号 BEGIN IF V_SAL > 50000 THEN RAISE E_SAL_TOO_HIGH; END IF; EXCEPTION WHEN E_SAL_TOO_HIGH THEN DBMS_OUTPUT.PUT_LINE('薪资超过限额'); END;

方式二:使用RAISE_APPLICATION_ERROR(最常用)

sql

CREATE OR REPLACE PROCEDURE P_UPDATE_SAL(P_EMPNO NUMBER, P_NEW_SAL NUMBER) IS BEGIN IF P_NEW_SAL < 0 THEN RAISE_APPLICATION_ERROR(-20001, '薪资不能为负数'); END IF; IF P_NEW_SAL > 50000 THEN RAISE_APPLICATION_ERROR(-20002, '薪资不能超过50000'); END IF; UPDATE EMP SET SAL = P_NEW_SAL WHERE EMPNO = P_EMPNO; COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; -- 写入日志表 INSERT INTO ERROR_LOG(ERR_DATE, ERR_CODE, ERR_MSG, PROC_NAME) VALUES(SYSDATE, SQLCODE, SQLERRM, 'P_UPDATE_SAL'); COMMIT; RAISE; -- 继续向上抛出 END;

注意:RAISE_APPLICATION_ERROR 的错误号范围:-20000-20999


补充3:异常处理的最佳实践(工作必备)

① 日志表示例

sql

CREATE TABLE ERROR_LOG ( LOG_ID NUMBER PRIMARY KEY, PROC_NAME VARCHAR2(100), ERR_CODE NUMBER, ERR_MSG VARCHAR2(4000), ERR_DATE DATE, USER_NAME VARCHAR2(30) ); CREATE SEQUENCE SEQ_ERROR_LOG;
② 带日志的存储过程模板

sql

CREATE OR REPLACE PROCEDURE P_BUSINESS(P_ID NUMBER) IS V_STEP VARCHAR2(100); BEGIN V_STEP := '查询用户信息'; -- 业务逻辑1 V_STEP := '更新订单状态'; -- 业务逻辑2 V_STEP := '发送通知'; -- 业务逻辑3 COMMIT; EXCEPTION WHEN OTHERS THEN ROLLBACK; INSERT INTO ERROR_LOG(LOG_ID, PROC_NAME, ERR_CODE, ERR_MSG, ERR_DATE, USER_NAME) VALUES(SEQ_ERROR_LOG.NEXTVAL, 'P_BUSINESS', SQLCODE, SQLERRM, SYSDATE, USER); COMMIT; -- 日志独立提交 -- 根据业务决定是否继续抛出 RAISE_APPLICATION_ERROR(-20099, '程序执行失败,失败步骤:' || V_STEP || ',原因:' || SQLERRM); END;
③ 异常处理策略决策树

text

发生异常 │ ├── 可预期且可恢复(如NO_DATA_FOUND) │ └── 捕获后给默认值或跳过,继续执行 │ ├── 可预期但不可恢复(如参数校验失败) │ └── RAISE_APPLICATION_ERROR 返回友好错误信息 │ ├── 不可预期异常 │ └── OTHERS捕获 → 写日志 → 回滚 → RAISE抛出 │ └── 事务性操作(多个DML) └── 异常必须ROLLBACK,保证数据一致性

补充4:高级异常处理技巧

① 异常传播机制

sql

BEGIN 子程序调用 -- 子程序内异常若未捕获,会传播到外层 EXCEPTION WHEN OTHERS THEN -- 外层可以再处理 END;
② 批量操作中的异常处理(常用)

sql

DECLARE TYPE T_EMP_ID IS TABLE OF EMP.EMPNO%TYPE; V_IDS T_EMP_ID := T_EMP_ID(7369, 7499, 9999, 7521); BEGIN FOR I IN 1..V_IDS.COUNT LOOP BEGIN -- 内层块隔离异常 UPDATE EMP SET SAL = SAL * 1.1 WHERE EMPNO = V_IDS(I); IF SQL%NOTFOUND THEN RAISE_APPLICATION_ERROR(-20001, '员工不存在:' || V_IDS(I)); END IF; EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('处理员工' || V_IDS(I) || '失败:' || SQLERRM); -- 继续处理下一个,不影响事务一致性 END; END LOOP; COMMIT; END;
③ 获取异常完整堆栈(11g+)

sql

DECLARE V_STACK CLOB; BEGIN -- 业务代码 RAISE ZERO_DIVIDE; EXCEPTION WHEN OTHERS THEN DBMS_UTILITY.FORMAT_ERROR_STACK(); -- 错误信息 DBMS_UTILITY.FORMAT_ERROR_BACKTRACE(); -- 调用堆栈(行号) DBMS_UTILITY.FORMAT_CALL_STACK(); -- 完整调用栈 END;

补充5:RAISE 的使用对比

写法含义
RAISE;重新抛出当前异常,保留原始错误信息
RAISE NO_DATA_FOUND;抛出指定的预定义异常
RAISE_APPLICATION_ERROR(code, msg);抛出自定义错误(不进入OTHERS?——会进入)

三、工作中的异常处理规范(推荐)

sql

CREATE OR REPLACE PROCEDURE 存储过程名(参数) IS -- 声明变量 V_ERR_MSG VARCHAR2(4000); BEGIN -- 1. 参数校验(主动抛异常) IF 参数不合法 THEN RAISE_APPLICATION_ERROR(-20001, '参数错误:...'); END IF; -- 2. 业务处理(每一段关键操作后COMMIT或统一提交) -- 3. 正常提交 COMMIT; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK; RAISE_APPLICATION_ERROR(-20002, '数据重复'); WHEN OTHERS THEN ROLLBACK; V_ERR_MSG := SQLERRM; -- 写日志 INSERT INTO ERROR_LOG VALUES(...); COMMIT; -- 重新抛出给调用方 RAISE; END;

四、总结对比表

维度原笔记补充说明
语法WHEN OTHERS THEN ...还可以按具体异常分类处理
异常信息SQLERRM还有SQLCODEDBMS_UTILITY.FORMAT_ERROR_BACKTRACE
异常后行为继续/回滚可以通过RAISE中断并向上传播
自定义异常未涉及RAISE_APPLICATION_ERROR是最常用方式
日志记录提到但未展开工作中写日志表是关键实践
批量场景未涉及内层块独立异常处理,让循环继续

如果您需要,我可以进一步提供:

  1. 异常日志表的完整建表语句和插入模板

  2. Spring Boot 调用 Oracle 存储过程时如何接收自定义异常

  3. 异常处理与事务边界的设计模式

http://www.jsqmd.com/news/787491/

相关文章:

  • 基于RAG-Agent架构的AI医疗助手:精准肿瘤治疗决策支持系统实践
  • 2026年比较好的钢结构设计制作安装/钢结构天桥/钢结构构件/钢结构加工热选公司推荐 - 行业平台推荐
  • 口碑好的装修公司哪家好? - 速递信息
  • 独立开发者如何借助Taotoken低成本接入多模型应对不同需求
  • Llama-Chinese中文优化实战:从数据构建到LoRA微调完整指南
  • 2026年靠谱的珍珠棉板材/天津珍珠棉拖盘/珍珠棉/珍珠棉拖盘口碑好的厂家推荐 - 品牌宣传支持者
  • 跟着 MDN 学 HTML day_30:(AbortController 实现可取消的异步请求)
  • AI——Dify创建第一个AI聊天机器人
  • LLM面试笔记深度解析:从Transformer到RAG的工程实践与高频考点
  • 基于Tauri+React的AI编码代理实时监控工具设计与实践
  • AI多智能体协作空间:从LangChain到Room项目的架构实践
  • 开发多模型测试平台以评估不同 AI 模型的任务表现
  • SQL 第四篇:JOIN 实战(数据库到底是怎么“拼表”的)
  • AGI驱动多模态AI在教育场景的应用实践与架构解析
  • 像素风健康应用开发:Vibe-Skills项目实战与设计解析
  • 如何用C语言解密网易云NCM音乐文件:实现跨平台音乐格式转换
  • AI编程助手代码审计工具whatdiditdo:从黑盒到白盒的智能复盘
  • 2026年口碑好的轻钢钢结构/钢结构构件/钢结构装配式建筑服务型公司推荐 - 品牌宣传支持者
  • CANN/pyasc:add_deq_relu API文档
  • 高速PCB设计中的EMI控制策略与实践
  • 2026年热门的苏州膜结构张拉膜棚/膜结构售后无忧公司 - 行业平台推荐
  • Zabbix AI技能实战:基于MCP协议实现自然语言监控运维自动化
  • 构建办公自动化CLI工具集:从Python库选型到实战应用
  • 【最新 v2.7.1 版本】OpenClaw v2.7.1 一键安装包|Windows 稳定极速部署
  • 构建AI模型路由框架:策略模式与统一端点抽象实践
  • BricksLLM:开源LLM API网关,解决大模型应用成本管控与用量追踪难题
  • ARM架构CSSELR_EL1寄存器:缓存管理与性能优化
  • 生成式AI在无障碍领域的应用:从技术潜力到工程实践
  • Syncia:基于浏览器扩展的AI助手,实现网页上下文智能处理与本地模型集成
  • 2026年靠谱的膜结构篮球馆棚/膜结构汽车棚可靠服务公司 - 行业平台推荐