Oracle 当其他人处理异常时

Oracle 当其他人处理异常时,oracle,exception,stored-procedures,plsql,Oracle,Exception,Stored Procedures,Plsql,背景: 我使用了一些Oracle文章来开发一个错误包,它由五个过程组成 其中两个是Log_和_Return以及Log_和_Continue。在整个程序中都会调用它们。每个都接受输入并将其传递给Handle过程。例如: PROCEDURE Log_And_Return (error_name) IS BEGIN Handle (error_name, TRUE, TRUE); END Log_And_Return; Handle过程然后根据传递给它的变量调用Log过程和Raise_To

背景:

我使用了一些Oracle文章来开发一个错误包,它由五个过程组成

其中两个是Log_和_Return以及Log_和_Continue。在整个程序中都会调用它们。每个都接受输入并将其传递给Handle过程。例如:

PROCEDURE Log_And_Return (error_name)
IS
BEGIN
    Handle (error_name, TRUE, TRUE);
END Log_And_Return; 
Handle过程然后根据传递给它的变量调用Log过程和Raise_To_应用程序过程,如下所示:

PROCEDURE Handle (error_name, log_error, reraise_error)    
IS
BEGIN
    // Code to fetch error code and message using error_name input parameter.
    IF log_error THEN
        LOG (error_code, error_message);
    END IF;

    IF in_reraise_error THEN
        Raise_To_Application (error_code, error_message);
    END IF;    
END Handle;
// bunch of logic

BEGIN
    UPDATE *another table*
    SET *some field* = *some value*
    WHERE *some field* = *variable passed into method*
EXCEPTION
WHEN NO_DATA_FOUND THEN
    Err.Log_And_Return('some_error')
END;
日志过程存储日期、stacktrace、错误代码、错误消息和id,最后Raise_To_应用程序过程执行以下操作:

raise_application_error (error_code, error_message);
问题:

我的问题是这个。假设我有一个执行查询的过程,例如获取客户记录。如果这个查询失败,这将是一个大问题。所以我可以这样做:

BEGIN    
    SELECT *something*
    FROM *some table*
    WHERE *some field* = *some user input*

    // more logic

EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');  
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');  
END;
这里,我的Log_和_返回过程接受输入,转到一个表并返回一个字符串以显示给用户。如果查询没有找到用户的记录,我有一个特定的错误,对于未知的错误,我有一个通用的错误。在这两种情况下,都会执行日志记录,记录错误的完整堆栈跟踪

然而,在我的示例中,我有一个“//更多逻辑”部分。比方说,我将该准则修改为:

BEGIN    
    SELECT *something* INTO *some variable*
    FROM *some table*
    WHERE *some field* = *user id*

    Call_Another_Procedure(*user id*, *some variable*)

EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');  
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');  
END;
现在,在select查询之后,我将调用另一个带有select查询结果的过程。在这个新查询中,我做了一些事情,包括一个update语句,如下所示:

PROCEDURE Handle (error_name, log_error, reraise_error)    
IS
BEGIN
    // Code to fetch error code and message using error_name input parameter.
    IF log_error THEN
        LOG (error_code, error_message);
    END IF;

    IF in_reraise_error THEN
        Raise_To_Application (error_code, error_message);
    END IF;    
END Handle;
// bunch of logic

BEGIN
    UPDATE *another table*
    SET *some field* = *some value*
    WHERE *some field* = *variable passed into method*
EXCEPTION
WHEN NO_DATA_FOUND THEN
    Err.Log_And_Return('some_error')
END;
问题:

我这里的问题是,如果查询没有返回任何结果,我抛出NO_DATA_FOUND错误,我记录问题,然后在我的“raise_To_application”过程中引发应用程序错误。。。这将被父过程中的“when others”子句捕获,这将向用户返回错误的消息

解决方法是什么?注意:如果需要发布更多代码,请告诉我

编辑:

我考虑过的一种解决方法(我不知道是否建议这样做)是使用BEGIN-END异常块包装每个存储过程,其中每个过程都有一个“When Others”块,该块只记录并重新运行最近的错误(即使用SQLCODE)。然后,在我的应用层中,我可以指定如果错误在-20000和-20999之间,则将其与消息一起显示,否则将显示一条通用消息(DBA可以通过查看日志表以及完整的堆栈跟踪来找出数据库中发生了什么)。有什么想法吗

编辑2:


如果有什么不合理的地方,我可以澄清。我已经对代码进行了大量更改和简化,以删除id参数和其他一些内容。

您需要使用
raise
重新引发底层过程中的错误

当发生
错误
时,如果有
异常块
,则句柄将移动到
异常块
。在您使用
raise
重新调用它之前,
调用方将保持不知道状态

将所有底层过程保留在
BEGIN-END
块中


也可以使用<代码> dBMSUntudio.FrastAtError OrthStase和 DbMSuUntudio.FrasATHyrRoRouthTrace获得调用堆栈。

< P>为其他异常考虑使用。有点像贝娄

create or replace trigger TRG_SERVERERROR 
   after servererror on database
declare
   <some_variable_for_logging_the_call_stack>
begin
   ERR.Log;
end;
在父过程中,您将处理当前特定过程的例外,而不是其他过程

BEGIN    
   SELECT *something* INTO *some variable*
   FROM *some table*
   WHERE *some field* = *user id*
   Call_Another_Procedure(*user id*, *some variable*)
EXCEPTION
   WHEN NO_DATA_FOUND THEN
      ERR.Raise(-20100, 'unknown user id');         
END;
从父过程调用的过程将只处理此特定过程的执行

BEGIN    
   SELECT *something*
   FROM *some table*
   WHERE *some field* = *some user input*

EXCEPTION
   WHEN NO_DATA_FOUND THEN
      ERR.Raise(-20100, 'unknown some user input');  
END;

在应用层,我们将有适当的消息——“uknown某些用户输入”或“未知用户id”。另一方面,触发器将记录有关特定异常的所有信息。

这几乎就是我一直使用的方法,因为我想记录代码中的每个入口和出口点:

application_error EXCEPTION;
PRAGMA EXCEPTION_INIT (application_error, -20000);

BEGIN    
    SELECT *something* INTO *some variable*
    FROM *some table*
    WHERE *some field* = *user id*

    Call_Another_Procedure(*user id*, *some variable*)

EXCEPTION
WHEN NO_DATA_FOUND THEN
    ERR.Log_And_Return('unknown_id');
WHEN application_error THEN -- ordinary exception raised by a subprocedure
    ERR.Log_And_Return('application_error');
    RAISE;
WHEN OTHERS THEN
    ERR.Log_And_Return('unknown_error');
    RAISE;
END;
对于子过程:

BEGIN
    UPDATE *another table*
    SET *some field* = *some value*
    WHERE *some field* = *variable passed into method*
EXCEPTION
WHEN NO_DATA_FOUND THEN
    Err.Log_And_Return('some_error');  -- this raises ORA-20000
END;

也许我的问题不清楚。我已经在做这些了。我的Log_和返回方法记录完整stacktrace的问题,然后执行raise_应用程序_错误。事实上,当它被提升时,调用程序中的另一个块将更改输出消息no,@Lalit是正确的。如果你必须有一个WHEN-OTHERS触发器,你可以记录错误,但是你应该用一个简单的
引发来结束它。这样,真正的、不可预测的、意外的未处理异常就会被引发给调用方。您的目标是为所有预测的、预期的异常添加处理程序。@Andrew Martin,您只需要在底层过程中使用RAISE,这样当发生错误时,它就会被引发并提供给调用方。这是主要程序。否则,您将多次记录错误并使自己难以调试。是的,但我希望根据错误发生的位置提供不同的错误消息,因此我不能只提出问题。我想提出一个具体的错误。如果我用一个特定的错误引发它,那么一旦它命中调用程序的“when others”块,该错误将被更改为在那里声明的任何错误-除非我简单地重新引发所有“when others”错误。现在,我的Log_和_返回过程执行此日志记录,然后引发应用程序错误。因此,我是否可以创建一个触发器来简单地检测是否抛出了应用程序错误,如果抛出了,则返回到应用程序层(即,它不需要担心日志记录,因为这已经完成了)?您可以有一个日志过程,它只会插入日志值(而不会引发)。从数据库引发的异常将返回到调用方实例。同样,我已经在做了。我的“Log And Return”调用一个日志过程,然后引发一个应用程序错误。那么,我是否希望获得一个触发器,该触发器将检测何时抛出应用程序错误并强制返回到应用程序层?请从父过程中删除所有when others子句。当您将不生成任何数据时