【问题标题】:Pl/SQL Nested Procedure Exception HandlingPl/SQL 嵌套过程异常处理
【发布时间】:2015-01-10 18:58:47
【问题描述】:

这是关于通过多个级别的 PL/SQL 过程进行错误处理的最佳实践问题。我查看了其他一些问题来帮助我,特别是 this one

目前,我有一个带有过程 1 的程序,它调用过程 2,它调用过程 3。我正在尝试执行足够的错误处理 - 但我想最终将确切的问题输出回应用层。我希望能就如何有效而清晰地做到这一点获得一些想法。

我目前的解决方法如下,但对我来说似乎相当混乱,有很多变量声明。我对 PL/SQL(以及一般的 SQL)非常陌生,所以我很感激任何关于以下方面的建议:

  1. 处理多层过程时的良好错误处理技术。
  2. 将错误消息反馈到应用层(在我下面的过程中,由“out_overall_output”变量表示。

程序流程:UI -> Proc 1 -> Proc 2 -> Proc 3

程序 1:

--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...

程序 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, 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;

注意:过程调用的层次比这更深。我展示的 sn-ps 大大简化了。我的真实程序中的错误信息和变量名更具描述性。

【问题讨论】:

  • 通常,您应该避免通过参数传递错误,而是使用RAISE 和/或RAISE_APPLICATION_ERROR 在过程之间传递错误。在您的示例中,您会在错误发生时立即掩盖错误,这意味着当您遇到意外错误时,您将不知道真正的错误是什么。
  • @Allan:您能否为我详细解释一下?据我了解,如果我在程序 3 中遇到“当其他人”错误,我会跟踪它并将其传递给链条。这是否意味着我不会知道真正的错误是什么?
  • 在上述最后一个过程中,如果出现OTHERS 错误,您总是会收到消息“调用过程2 时出错”。是数字溢出错误吗?类型转换错误?分布式事务错误?从字面上看,它可能有数以千计的错误消息。您通过将错误替换为通用消息来隐藏所有这些信息。

标签: oracle stored-procedures plsql exception-handling


【解决方案1】:

要显示应用程序级别的“服务器会发生什么”的确切解释,您可以尝试以下操作。在程序中:

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;

在顶级程序中:

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;

p1 出现异常后,您将看到如下内容:

ORA-20003: Purchasing of "Cool red Ferrari" cancelled
ORA-20002: Action "car purchase" is not completed
ORA-20001: Client with ID 123 has no right to perform action "Spent all money of Bill Gates"

带有false 值的过程raise_application_error 的第三个参数会删除所有先前的错误消息。如果您将在过程p3 中使用错误值,则在此示例中您将只看到一条代码为ORA-20003 的错误消息。

P。 S. 您也可以定义自己的异常并在WHEN .. THEN 子句中使用它们。在这里您可以找到更多信息和示例:https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/errors.htm#LNPLS00704

P。 P.S. 如何登录。记录过程:

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);

【讨论】:

  • 出于好奇,您建议在哪里登录您提供的示例?就在每次引发应用程序错误之前,还是在顶层的最后?
  • 如果答案是在顶层进行日志记录,您如何记录整个 raise_application_error sn-p?
  • 在这个例子(和大多数其他例子)中,我认为最好在顶层做一次日志记录(一个事务而不是三个),但我不能说它是唯一可能的解决方案。
  • 我在帖子中添加了示例。 dbms_utility.format_error_backtrace 是一个函数,它在过程调用期间返回异常的“完整故事”。你可以把它的值放到一个变量中,然后做你想做的一切。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-26
  • 1970-01-01
相关资源
最近更新 更多