Oracle PL/SQL中多个特定异常的共享代码

Oracle PL/SQL中多个特定异常的共享代码,oracle,plsql,Oracle,Plsql,我是PL/SQL的新手,正在尝试为我编写的包实现一些异常处理 我遇到了一种情况,我可能会提出多个例外 在引发这些异常的情况下,我可能希望针对每个异常执行特定的操作,例如,如果引发异常A,则关闭并删除一个文件,但如果引发异常B,则我只需关闭光标并发送电子邮件来警告某人发生了致命错误 这很好,我知道如何使用,然后 我遇到的问题是,我无法编写泛型代码,因为在发生异常的情况下,任何异常都会被引发,以执行我始终希望执行的操作,例如写入日志文件。这意味着我必须为每个异常类型复制代码行,如下所示 DECLAR

我是PL/SQL的新手,正在尝试为我编写的包实现一些异常处理

我遇到了一种情况,我可能会提出多个例外

在引发这些异常的情况下,我可能希望针对每个异常执行特定的操作,例如,如果引发异常A,则关闭并删除一个文件,但如果引发异常B,则我只需关闭光标并发送电子邮件来警告某人发生了致命错误

这很好,我知道如何使用
,然后

我遇到的问题是,我无法编写泛型代码,因为在发生异常的情况下,任何异常都会被引发,以执行我始终希望执行的操作,例如写入日志文件。这意味着我必须为每个异常类型复制代码行,如下所示

DECLARE
    test_exception EXCEPTION;
BEGIN
    --some code
    RAISE test_exception;
    --some code
EXCEPTION        
    WHEN test_exception THEN        
        SEND_EMAIL('Something went wrong');
        WRITE_TO_LOG('Error ' || SQLCODE || ' | ' || SUBSTR(SQLERRM, 1, 200) || ' | ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
    WHEN OTHERS THEN     
        SOME_OTHER_FUNCTION();
        WRITE_TO_LOG('Error ' || SQLCODE || ' | ' || SUBSTR(SQLERRM, 1, 200) || ' | ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE); 
END;
我想要实现的是类似于此的东西,它不会编译

DECLARE
    test_exception EXCEPTION;
BEGIN
    --some code
    RAISE test_exception;
    --some code
EXCEPTION        

    WRITE_TO_LOG('Error ' || SQLCODE || ' | ' || SUBSTR(SQLERRM, 1, 200) || ' | ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);

    WHEN test_exception THEN        
        SEND_EMAIL('Something went wrong');
    WHEN OTHERS THEN      
        SOME_OTHER_FUNCTION();
END;
显然,这个例子并不完整,但给出了一个粗略的想法。对于这样一个重复的行来说,这不是问题,但是如果有许多游标或文件要关闭,或者其他内务管理,这似乎有点冗长/乏味

是否有
WHEN ALL THEN
条款或类似条款?这是某种
GOTO
的用例吗?还是我试图在这里应用错误的概念

我似乎找不到有人问同样的问题,这对我来说意味着我以错误的方式处理这个问题,或者我缺少一些基本知识


如果我理解正确的话,谢谢你-解决方法很简单。引发异常时,可以在嵌套块中处理它,并通过
RAISE

    DECLARE
    test_exception EXCEPTION;
begin
      RAISE test_exception;

    --some code
EXCEPTION        
    WHEN OTHERS THEN
      BEGIN
         dbms_output.put_line( 'WRITE_TO_LOG');
         RAISE;
      EXCEPTION
        WHEN test_exception THEN
          dbms_output.put_line( 'test_exception');
         WHEN OTHERS THEN
        dbms_output.put_line( 'some other function');
     end;
END;
输出:

WRITE_TO_LOG
send mail

PL/SQL将只执行一个异常块。因此,如果您有一些要针对每个异常运行的通用代码(例如日志记录),则必须在其他异常运行时在
中运行

然后检查
sqlcode
,以执行特定于异常的处理。例如:

begin

  raise TOO_MANY_ROWS;

exception
  when others then
    dbms_output.put_line ( 'Generic stuff' );
    case sqlcode 
      when -1422 then
        dbms_output.put_line ( 'TMR specific' );
      when -1476 then
        dbms_output.put_line ( 'ZD specific' );
      else
        null;
    end case;

    raise;
end;
/

Generic stuff
TMR specific

ORA-01422: exact fetch returns more than requested number of rows

begin

  raise ZERO_DIVIDE;

exception
  when others then
    dbms_output.put_line ( 'Generic stuff' );
    case sqlcode 
      when -1422 then
        dbms_output.put_line ( 'TMR specific' );
      when -1476 then
        dbms_output.put_line ( 'ZD specific' );
      else
        null;
    end case;

    raise;
end;
/

Generic stuff
ZD specific

ORA-01476: divisor is equal to zero
现在,这是否是一个好主意还有争议

当其他异常块以某种方式重新引发异常时。这可以防止您抑制严重的意外错误(如磁盘空间不足)。但对于某些特定的例外情况,您可能需要继续处理


附录

如果您有用户定义的异常,如果您想要处理这些异常,您需要做一些额外的工作。一种方法是创建一个标准的异常包。在其中,为您将使用的所有异常命名、初始化和定义值常量

然后,您可以将这些定义用于用户定义的异常:

create or replace package excepts as 

  user_defined exception ;
  user_defined_val pls_integer := -20001;
  pragma exception_init ( user_defined, -20001 );

end;
/

begin

  raise excepts.USER_DEFINED;

exception
  when others then
    dbms_output.put_line ( 'Generic stuff' );
    case sqlcode 
      when -1422 then
        dbms_output.put_line ( 'TMR specific' );
      when -1476 then
        dbms_output.put_line ( 'ZD specific' );
      when excepts.user_defined_val then
        dbms_output.put_line ( 'User-defined specific' );
      else
        null;
    end case;

    raise;
end;
/

Generic stuff
User-defined specific

ORA-20001: 

是的,我已经为此奋斗了很多年。我认为甲骨文的异常处理有一个很大的漏洞。IDK是一个很好的解决方案。谢谢你的回复,我不知道这有多详细,但我明白你的意思。然而,这肯定更难维护和理解,特别是对于最终可能正在开发它的其他开发人员。想象一下,如果在一个复杂的包中有大量用户定义的异常,入口点块是否必须在一个地方处理所有这些冒泡的异常?它还要求所有例外情况都要在全球范围内定义,而不是在合理的范围内。我同意也不同意。。我很难解释我自己。我已经更新了我的答案,也许这样会更好。接近语句时全部:)编辑:我在一个基于pl/sql的非常大的系统中工作。在这里,我们不会在每个场景中声明一个异常。我们根据情况声明1或2,在提出它之前,我们添加了一些信息,如l_error:=“没有…”;使用dbms_utility.format_error_backtraceAgree时很容易调试这是Oracle框架内唯一的方法,但问题是他的用户定义异常无法预测其SQLCode值,除非他使用raise_application_error而不是用户定义的异常类型。然后他就可以定义并捕获自己的代码了。谢谢你的回复。我可以看到,只有当其他人使用
时,我才能使用
,并使用
PRAGMA EXCEPTION_INIT
定义我自己的异常来设置代码,然后有效地“切换”该代码,但这既不可读,也不可大量维护(因此我相信你的评论是有争议的);一种简化方法是在包中定义所有异常,然后在代码中引用。为了解决这个问题,我更新了答案。我定义了很多明确的、全局定义的异常(我通常在PRAGMA上使用RAISE\u APPLICATION\u ERROR),原因之一是,您不必担心它的范围。您不能轻易地将命名异常重新发送到外部作用域-或者,至少外部作用域无法通过名称识别它!回答得好,克里斯。很抱歉打扰你一个问题,我刚刚在读这篇文章的时候想起了这个问题。Oracle中是否有异常及其相应错误消息的数据字典?我可以找到列出所有错误代码的文档。但是,我真的希望为数据库用户提供一个紧凑的存储库,比如一个元数据表,它将这些信息保存在一个地方。