PL/SQL RAISE_APPLICATION_ERROR 参数问题

Posted

技术标签:

【中文标题】PL/SQL RAISE_APPLICATION_ERROR 参数问题【英文标题】:PL/SQL RAISE_APPLICATION_ERROR parameter question 【发布时间】:2020-03-16 17:50:48 【问题描述】:

我是 PL/SQL 新手,我不完全理解 RAISE_APPLICATION_ERROR 的错误代码参数。

在文档中它说The error_number is a negative integer with the range from -20999 to -20000。但这并不能解释我是否可以选择随机数或者是否有系统。 另外我想知道在调用 RAISE_APPLICATION_ERROR 时是否可以在不同的函数中重复使用相同的数字?或者该数字会在某处用于识别特定错误?

我也在查看某人编写的程序,想知道他们为什么不直接将 SQLCODE 传递给 RAISE_APPLICATION_ERROR 而不是像 RAISE_APPLICATION_ERROR(l_err_code, 'Record already exists!'); 那样采用随机数

我的印象是用户定义的错误只使用整数 -20999 到 -20000,而不是现有错误。

PROCEDURE insert_record
(v_row IN OUT TABLE1%ROWTYPE) IS

 l_err_code NUMBER;
 l_err_message VARCHAR(200);
 BEGIN 


    INSERT INTO TABLE1 ....
      RETURNING id INTO V_row.id;

 EXCEPTION      
    WHEN DUP_VAL_ON_INDEX THEN
       l_err_code := SQLCODE;  
       l_err_message  := 'Insert Error: ' - ' || SQLERRM;   

       ...logging error here

      RAISE_APPLICATION_ERROR(-20001, 'Record already exists!');    
  WHEN OTHERS THEN 
    l_err_code := SQLCODE;
    l_err_message  := 'Insert Error: ' - ' || SQLERRM;   

     --- loggin error here

    RAISE_APPLICATION_ERROR(-20002, l_err_message);      

END insert_record;

将上面的函数修改如下有意义吗?:

PROCEDURE insert_record
(v_row IN OUT TABLE1%ROWTYPE) IS

 l_err_code NUMBER;
 l_err_message VARCHAR(200);
 BEGIN 


    INSERT INTO TABLE1 ....
      RETURNING id INTO V_row.id;

 EXCEPTION      
    WHEN DUP_VAL_ON_INDEX THEN
       l_err_code := SQLCODE;  
       l_err_message  := 'Record already exists!';   

       ...logging error here

      RAISE_APPLICATION_ERROR(l_err_code, l_err_message);    
  WHEN OTHERS THEN 
    l_err_code := SQLCODE;
    l_err_message  := 'Insert Error: ' - ' || SQLERRM;   

     --- loggin error here

    RAISE_APPLICATION_ERROR(l_err_code, l_err_message);      

END insert_record;

如果有人可以为我回答这些问题或向我指出一些澄清这些问题的文档,我将不胜感激。

【问题讨论】:

顺便说一下,请记住,Oracle APEX 有时会在此范围内引发异常,因此如果您尝试使用 SQLCODE 来引用特定的应用程序错误,请务必小心。就我个人而言,我倾向于只使用 -20000(“用户错误”)和 -20001(“系统错误”)并将更多有用的信息嵌入到错误消息本身中。 YMMV。 【参考方案1】:

RAISE_APPLICATION_ERRORerror_code 参数仅接受 -20999 到 -20000 范围内的数字。在 -20999 到 -20000 之间,您可以随意使用错误编号,没有任何限制。数字的任何“组织”取决于您。如果您传递超出该范围的数字,则会引发 不同 异常 - ORA-21000: error number argument to raise_application_error of -xxxxx is out of range。例如:

BEGIN
  RAISE_APPLICATION_ERROR(-54321, 'This is not a valid error code');
END;

导致异常

ORA-21000: error number argument to raise_application_error of -54321 is out of range
ORA-06512: at line 2

被提升。 db<>fiddle here

请注意,由于 Oracle 生成的 SQLCODE 值总是超出RAISE_APPLICATION_ERROR 允许的范围,因此您不能将 SQLCODE 传递给 RAISE_APPLICATION_ERROR 否则您将得到相同的 ORA-21000 异常,这反而会失败将 SQLCODE 传递给RAISE_APPLICATION_ERROR 的目的。重新引发系统定义的异常(例如 DUP_VAL_ON_INDEX)的正确方法是使用 RAISE 语句的无参数版本。例如,让我们稍微重写您的代码:

PROCEDURE insert_record
(v_row IN OUT TABLE1%ROWTYPE) IS

 l_err_code NUMBER;
 l_err_message VARCHAR(200);
 BEGIN 


    INSERT INTO TABLE1 ....
      RETURNING id INTO V_row.id;

 EXCEPTION      
    WHEN DUP_VAL_ON_INDEX THEN
       l_err_code := SQLCODE;  
       l_err_message  := 'Record already exists!';   

       -- log error here

       -- Now, re-raise the exception so an outer handler can deal with it.
       -- Note that there's no way to include a new error message in this case.

       RAISE;

    WHEN OTHERS THEN 
      l_err_code := SQLCODE;
      l_err_message  := 'Insert Error: ' - ' || SQLERRM;   

       --- log error here

      -- Now, re-raise the exception so an outer handler can deal with it
      RAISE;    
END insert_record;

您可以通过创建EXCEPTION 变量来使用其他错误编号,使用EXCEPTION_INIT pragma 为它们分配异常编号,然后使用RAISE 语句引发这些异常。例如,

DECLARE
  my_exception EXCEPTION;

  PRAGMA EXCEPTION_INIT(my_exception, -54321);
BEGIN
  RAISE my_exception;
EXCEPTION
  WHEN my_exception THEN
    DBMS_OUTPUT.PUT_LINE('my_exception caught!');
END;

产生输出

my_exception caught!

db<>fiddle here

PRAGMA EXCEPTION_INIT 允许 -10000000 到 -1 范围内的值,但 -1403 除外。 100也是允许的。

【讨论】:

【参考方案2】:

您可以为自己的应用程序错误选择该范围内的任何数字。重复数字和细微不同的错误文本消息肯定会造成真正的混乱。您定义的错误编号可能是业务逻辑错误未知的错误,可能的错误,例如太多的行。

这里有一个 really good detailed explanation 和 Steven Feurstein 的 here,他在其中概述了适当的范围。

错误处理值得花一些时间思考并导致一些抽象问题,例如:

如果出现错误,应用程序能否继续处理? 如果不是,那么您需要停止处理,因为您不希望处理不完整或部分交易 您需要哪些信息来跟踪发生错误的位置? (通常是包名称、函数/过程名称、行号、违规代码、引发异常时使用的参数。

根据我的经验,大多数应用程序不能也不应该在调用任何类型的异常时继续处理事务。那时您希望将所有详细信息记录到表中,停止进一步处理并将应用程序重定向到“抱歉,发生错误”页面。

对于上面的代码,我会删除When Others。您知道可能发生的错误,例如值过多。您想要记录详细信息并停止处理。对于未知错误,我宁愿让错误从过程或函数调用中冒出来,也不愿看到它可能被错误地归类为已知错误。

【讨论】:

【参考方案3】:

您是正确的,因为 20000 和 20999 之间的代码用于用户定义的错误。除非您需要在错误处理中添加一些其他基本级别的详细信息,否则无需将此类错误代码强制添加到 Oracle 已定义的条件/响应中。

【讨论】:

但是当我想向应用程序返回错误时,我可以将 SQLCODE 传递给 RAISE_APPLICATION_ERROR 吗? 是的。这是标准做法。检查这些链接以获取来自 PL/SQL Master 的更多信息:blogs.oracle.com/oraclemagazine/error-managementstevenfeuersteinonplsql.blogspot.com/2016/03/…stevenfeuersteinonplsql.blogspot.com/2017/02/… 但上面提到了将 SQLCODE 传递给 RAISE_APPLICATION_ERROR 将产生 ORA-21000 异常,因为这些超出了允许的范围 啊,我误解了你的问题。您确实需要使用 RAISE_APPLICATION_ERROR,只需 RAISE。 RAISE_APPLICATION_ERROR 仅用于您的自定义错误。

以上是关于PL/SQL RAISE_APPLICATION_ERROR 参数问题的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL编程_变量

PL/SQL学习笔记_01_基础

PL/SQL编程_子程序设计

PL/SQL - 如何从连接表中返回单行

PL/SQL学习笔记_03_存储函数与存储过程

PL/SQL 上机练习