【问题标题】:Oracle PL/SQL - Raise User-Defined Exception With Custom SQLERRMOracle PL/SQL - 使用自定义 SQLERRM 引发用户定义的异常
【发布时间】:2011-08-26 14:25:03
【问题描述】:

是否可以创建用户定义的异常并能够更改 SQLERRM?

例如:

DECLARE
    ex_custom       EXCEPTION;
BEGIN
    RAISE ex_custom;
EXCEPTION
    WHEN ex_custom THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

输出是“用户定义的异常”。是否可以更改该消息?

编辑:这里有更多细节。

我希望这篇文章能说明我正在努力做得更好。

DECLARE
    l_table_status      VARCHAR2(8);
    l_index_status      VARCHAR2(8);
    l_table_name        VARCHAR2(30) := 'TEST';
    l_index_name        VARCHAR2(30) := 'IDX_TEST';
    ex_no_metadata      EXCEPTION;
BEGIN

    BEGIN
        SELECT  STATUS
        INTO    l_table_status
        FROM    USER_TABLES
        WHERE   TABLE_NAME      = l_table_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Table metadata does not exist."
            RAISE ex_no_metadata;
    END;

    BEGIN
        SELECT  STATUS
        INTO    l_index_status
        FROM    USER_INDEXES
        WHERE   INDEX_NAME      = l_index_name;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            -- raise exception here with message saying
            -- "Index metadata does not exist."
            RAISE ex_no_metadata;
    END;

EXCEPTION
    WHEN ex_no_metadata THEN
        DBMS_OUTPUT.PUT_LINE('Exception will be handled by handle_no_metadata_exception(SQLERRM) procedure here.');
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
/

实际上,有几十个这样的子块。我想知道是否有一种方法可以为每个要引发的子块设置一个用户定义的异常,但让它给出不同的消息,而不是为每个子块创建一个单独的用户定义的异常。

在 .NET 中,这有点像这样的自定义异常:

    public class ColorException : Exception
    {
        public ColorException(string message)
            : base(message)
        {
        }
    }

然后,一个方法会是这样的:

        if (isRed)
        {
            throw new ColorException("Red is not allowed!");
        }

        if (isBlack)
        {
            throw new ColorException("Black is not allowed!");
        }

        if (isBlue)
        {
            throw new ColorException("Blue is not allowed!");
        }

【问题讨论】:

    标签: oracle exception plsql custom-exceptions


    【解决方案1】:

    是的。你只需要使用RAISE_APPLICATION_ERROR 函数。如果您还想命名您的异常,则需要使用EXCEPTION_INIT pragma 以便将错误号与命名的异常相关联。类似的东西

    SQL> ed
    Wrote file afiedt.buf
    
      1  declare
      2    ex_custom EXCEPTION;
      3    PRAGMA EXCEPTION_INIT( ex_custom, -20001 );
      4  begin
      5    raise_application_error( -20001, 'This is a custom error' );
      6  exception
      7    when ex_custom
      8    then
      9      dbms_output.put_line( sqlerrm );
     10* end;
    SQL> /
    ORA-20001: This is a custom error
    
    PL/SQL procedure successfully completed.
    

    【讨论】:

    • 正是我需要的!我想我在您已经回答我的问题时进行了编辑。非常感谢。
    【解决方案2】:

    您可以像这样使用 RAISE_APPLICATION_ERROR:

    DECLARE
        ex_custom       EXCEPTION;
    BEGIN
        RAISE ex_custom;
    EXCEPTION
        WHEN ex_custom THEN
            RAISE_APPLICATION_ERROR(-20001,'My exception was raised');
    END;
    /
    

    这将引发如下异常:

    ORA-20001: My exception was raised
    

    错误号可以是 -20001 到 -20999 之间的任何值。

    【讨论】:

      【解决方案3】:

      我通常会忘记我所有的-20001-type 错误代码,所以我尝试将我所有的应用程序错误合并到一个不错的包中,如下所示:

      SET SERVEROUTPUT ON
      
      CREATE OR REPLACE PACKAGE errors AS
        invalid_foo_err EXCEPTION;
        invalid_foo_num NUMBER := -20123;
        invalid_foo_msg VARCHAR2(32767) := 'Invalid Foo!';
        PRAGMA EXCEPTION_INIT(invalid_foo_err, -20123);  -- can't use var >:O
      
        illegal_bar_err EXCEPTION;
        illegal_bar_num NUMBER := -20156;
        illegal_bar_msg VARCHAR2(32767) := 'Illegal Bar!';
        PRAGMA EXCEPTION_INIT(illegal_bar_err, -20156);  -- can't use var >:O
      
        PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL);
      END;
      /
      
      CREATE OR REPLACE PACKAGE BODY errors AS
        unknown_err EXCEPTION;
        unknown_num NUMBER := -20001;
        unknown_msg VARCHAR2(32767) := 'Unknown Error Specified!';
      
        PROCEDURE raise_err(p_err NUMBER, p_msg VARCHAR2 DEFAULT NULL) AS
          v_msg VARCHAR2(32767);
        BEGIN
          IF p_err = unknown_num THEN
            v_msg := unknown_msg;
          ELSIF p_err = invalid_foo_num THEN
            v_msg := invalid_foo_msg;
          ELSIF p_err = illegal_bar_num THEN
            v_msg := illegal_bar_msg;
          ELSE
            raise_err(unknown_num, 'USR' || p_err || ': ' || p_msg);
          END IF;
      
          IF p_msg IS NOT NULL THEN
            v_msg := v_msg || ' - '||p_msg;
          END IF;
      
          RAISE_APPLICATION_ERROR(p_err, v_msg);
        END;
      END;
      /
      

      然后调用errors.raise_err(errors.invalid_foo_num, 'optional extra text')使用它,像这样:

      BEGIN
        BEGIN
          errors.raise_err(errors.invalid_foo_num, 'Insufficient Foo-age!');
        EXCEPTION
          WHEN errors.invalid_foo_err THEN
            dbms_output.put_line(SQLERRM);
        END;
      
        BEGIN
          errors.raise_err(errors.illegal_bar_num, 'Insufficient Bar-age!');
        EXCEPTION
          WHEN errors.illegal_bar_err THEN
            dbms_output.put_line(SQLERRM);
        END;
      
        BEGIN
          errors.raise_err(-10000, 'This Doesn''t Exist!!');
        EXCEPTION
          WHEN OTHERS THEN
            dbms_output.put_line(SQLERRM);
        END;
      END;
      /
      

      产生这个输出:

      ORA-20123: Invalid Foo! - Insufficient Foo-age!
      ORA-20156: Illegal Bar! - Insufficient Bar-age!
      ORA-20001: Unknown Error Specified! - USR-10000: This Doesn't Exist!!
      

      【讨论】:

      • 不错的提示!这对我的项目非常有用!
      • 这是一个很好的做法。 raise_application_error 的两个小问题认为:1)第二个参数的大小限制为 2048 字节,2)我希望第三个参数是 true(而不是默认的 false)以获得完整的堆栈跟踪。
      【解决方案4】:
      declare
         z exception;
      
      begin
         if to_char(sysdate,'day')='sunday' then
           raise z;
         end if;
      
         exception 
           when z then
              dbms_output.put_line('to day is sunday');
      end;
      

      【讨论】:

        【解决方案5】:
        create or replace PROCEDURE PROC_USER_EXP 
        AS
        duplicate_exp EXCEPTION;
        PRAGMA EXCEPTION_INIT( duplicate_exp, -20001 );
        LVCOUNT NUMBER;
        BEGIN
          SELECT COUNT(*) INTO LVCOUNT FROM JOBS WHERE JOB_TITLE='President';
          IF LVCOUNT >1 THEN 
           raise_application_error( -20001, 'Duplicate president customer excetpion' );
          END IF;
        
          EXCEPTION 
          WHEN duplicate_exp THEN 
          DBMS_OUTPUT.PUT_LINE(sqlerrm);
        END PROC_USER_EXP;
        

        ORACLE 11g 的输出会是这样的:

        Connecting to the database HR. 
        ORA-20001: Duplicate president customer excetpion 
        Process exited. 
        Disconnecting from the database HR
        

        【讨论】:

        • ORACLE 11g 输出将是这样的:- 连接到数据库 HR。 ORA-20001:重复的总裁客户异常进程退出。与数据库 HR 断开连接。
        • 这是个好东西,除了(讽刺的是)EXCEPTION 块。在几乎所有情况下,在 WHEN 子句中处理的异常都应该重新引发到调用程序。调用程序需要知道被调用例程失败。仅仅使用 DBMS_OUTPUT 还不够好,因为调用程序可能无法处理服务器输出缓冲区和/或因为它不会强制调用程序确认错误。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-26
        • 1970-01-01
        • 1970-01-01
        • 2012-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多