Oracle Pl/SQL嵌套过程异常处理
这是一个关于通过多个级别的PL/SQL过程进行错误处理的最佳实践问题。我已经看了一些其他的问题来帮助我,特别是 目前,我有一个带有过程1的程序,它调用过程2,它调用过程3。我试图执行适当的错误处理,但我希望最终将确切的问题输出回应用层。我希望能得到一些关于如何有效和清晰地做到这一点的想法 我当前的解决方案方法如下所示,但对我来说似乎相当混乱,有很多变量声明。我对PL/SQL(以及一般的SQL)非常陌生,因此我非常感谢您对以下方面的建议:Oracle Pl/SQL嵌套过程异常处理,oracle,stored-procedures,plsql,exception-handling,Oracle,Stored Procedures,Plsql,Exception Handling,这是一个关于通过多个级别的PL/SQL过程进行错误处理的最佳实践问题。我已经看了一些其他的问题来帮助我,特别是 目前,我有一个带有过程1的程序,它调用过程2,它调用过程3。我试图执行适当的错误处理,但我希望最终将确切的问题输出回应用层。我希望能得到一些关于如何有效和清晰地做到这一点的想法 我当前的解决方案方法如下所示,但对我来说似乎相当混乱,有很多变量声明。我对PL/SQL(以及一般的SQL)非常陌生,因此我非常感谢您对以下方面的建议: 处理多层过程时,良好的错误处理技术 将错误消息反馈到应用程
--One input variable, one output.
in_id VARCHAR2;
out_overall_output VARCHAR2;
...
DECLARE
l_success BOOLEAN;
l_error_output VARCHAR2(100);
BEGIN
Proc2(id, l_success, l_error_output);
IF l_success = FALSE THEN
out_overall_output = l_error_output
END IF
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
out_overall_output:= 'Error calling Proc 2'
RETURN;
END;
--Normal flow continues if l_success is true...
--One input variable, two output.
in_id VARCHAR2;
out_success BOOLEAN;
out_error_message VARCHAR2;
...
BEGIN
DELETE
FROM table
WHERE id = in_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
out_success = FALSE;
out_error_message = 'Error - No data to delete'
WHEN OTHERS THEN
out_success = FALSE;
out_error_message = 'Error deleting data.'
END;
程序2:
-- One input variable, two output.
in_id
out_success
out_error_output
//other logic
DECLARE
l_success BOOLEAN;
l_error_output VARCHAR2(100)
BEGIN
Proc3(id, l_success, l_error_output)
IF l_success = FALSE THEN
out_error_output = l_error_output
END IF
EXCEPTION
WHEN OTHERS
out_error_output = 'Error calling Proc 3'
RETURN;
END;
程序3:
--One input variable, one output.
in_id VARCHAR2;
out_overall_output VARCHAR2;
...
DECLARE
l_success BOOLEAN;
l_error_output VARCHAR2(100);
BEGIN
Proc2(id, l_success, l_error_output);
IF l_success = FALSE THEN
out_overall_output = l_error_output
END IF
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
out_overall_output:= 'Error calling Proc 2'
RETURN;
END;
--Normal flow continues if l_success is true...
--One input variable, two output.
in_id VARCHAR2;
out_success BOOLEAN;
out_error_message VARCHAR2;
...
BEGIN
DELETE
FROM table
WHERE id = in_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
out_success = FALSE;
out_error_message = 'Error - No data to delete'
WHEN OTHERS THEN
out_success = FALSE;
out_error_message = 'Error deleting data.'
END;
注意:过程调用的级别比这更深。我所展示的代码片段大大简化了。我实际过程中的错误消息和变量名更具描述性。要展示应用程序级别“服务器发生了什么”的确切解释,可以尝试以下操作。在过程中:
create or replace procedure p1 is
...
exception
when <some_error> then
<do something>
-- re-raise error:
raise_application_error(-20001, 'Client with ID '|| ID || ' has no right to perform action "' || ACTION_NAME || '"', true);
end;
create or replace procedure p2 is
begin
p1;
exception
when <another_error> then
<do something>
-- re-raise error:
raise_application_error(-20002, 'Action "' || ACTION_NAME || '" is not completed', true);
end;
create or replace procedure p3 is
begin
p2;
exception
when <another_error> then
<do something>
-- re-raise error:
raise_application_error(-20003, 'Purchasing of "' || CAR_NAME || '" cancelled', true);
end;
程序raise\u application\u error
withfalse
值的第三个参数将删除所有以前的错误消息。如果在程序p3
中使用false值,在本例中,您将只看到一条带有代码ORA-20003
的错误消息
另外,您还可以定义自己的异常,并在WHEN..THEN
子句中使用它们。在这里您可以找到更多信息和示例:
p.p.S.如何记录日志。日志过程:
create or replace procedure top_level_procedure is
begin
p1;
exception
when <one_more_error> then
<do something>
raise_application_error(-20004, dbms_utility.format_error_backtrace);
end;
create or replace procedure log(p_log_message varchar2) is
pragma autonomous_transaction;
begin
insert into log_table(..., log_message) values (..., p_log_message);
commit;
end;
when <one_more_error> then
<do something>
log(..., dbms_utility.format_error_backtrace);
raise_application_error(-20004, dbms_utility.format_error_backtrace);
调用日志过程:
create or replace procedure top_level_procedure is
begin
p1;
exception
when <one_more_error> then
<do something>
raise_application_error(-20004, dbms_utility.format_error_backtrace);
end;
create or replace procedure log(p_log_message varchar2) is
pragma autonomous_transaction;
begin
insert into log_table(..., log_message) values (..., p_log_message);
commit;
end;
when <one_more_error> then
<do something>
log(..., dbms_utility.format_error_backtrace);
raise_application_error(-20004, dbms_utility.format_error_backtrace);
什么时候
日志(…,dbms_实用程序。格式_错误_回溯);
引发应用程序错误(-20004,dbms实用程序。格式化错误\u回溯);
通常,您应该避免通过参数传递错误,而是使用
RAISE
和/或RAISE\u APPLICATION\u ERROR
在过程之间传递错误。在您的示例中,您会在错误发生时立即掩盖错误,这意味着当您遇到意外错误时,您不会知道真正的错误是什么。@Allan:你能给我解释得更详细一点吗?据我所知,如果我在过程3中遇到“when others”错误,我会跟踪并将其传递到链上。这不意味着我不知道真正的错误是什么吗?在上面的最后一个过程中,如果发生others
错误,你总能得到消息“调用进程2时出错"。这是数字溢出错误吗?类型转换错误吗?分布式事务错误吗?可能有成千上万条错误消息。您通过将错误替换为通用消息来隐藏所有这些信息。出于好奇,您建议在何处登录您给出的示例?就在引发问题之前每次都会出现应用程序错误,或者在顶层的最后出现错误?如果答案是在顶层进行日志记录,那么如何记录整个raise_应用程序_错误片段?在本例中(以及大多数其他示例),我认为最好在顶层进行一次日志记录(一个事务而不是三个事务),但我不能说这是唯一可能的解决方案。我在post中添加了示例。dbms\u实用程序。format\u error\u backtrace
是一个函数,在过程调用期间返回异常的“完整故事”。您可以将其值放入变量,然后执行所有您想要的操作。