在Oracle程序中使用OUT参数捕获错误

时间:2017-08-05 14:52:47

标签: oracle stored-procedures plsql oracle11g

我必须创建将行插入表的过程,我还需要包含可以捕获任何错误的am OUT参数。但是,当我使用匿名块进行测试时,它将无法工作,但如果我在过程中使用异常则可以正常工作。这意味着如果我添加一个OUT参数并从块中传递值,它就不会起作用。

此代码有效,但不是我想要的:

    create or replace 
    PROCEDURE EXAM_SP 
(P_FIRSTNAME IN BB_SHOPPER.FIRSTNAME%TYPE,
 P_LASTNAME  IN BB_SHOPPER.LASTNAME%TYPE,
 P_ADDRESS IN BB_SHOPPER.ADDRESS%TYPE,
 P_CITY IN BB_SHOPPER.CITY%TYPE,
 P_STATE IN BB_SHOPPER.STATE%TYPE,
 P_ZIP IN BB_SHOPPER.ZIPCODE%TYPE)


IS  

BEGIN
INSERT INTO BB_SHOPPER (IDSHOPPER, FIRSTNAME, LASTNAME, ADDRESS, CITY,   
STATE, ZIPCODE)
VALUES                   
(BB_SHOPPER_IDSHOPPER_SEQ.NEXTVAL,P_FIRSTNAME,P_LASTNAME,P_ADDRESS, 
                        P_CITY, P_STATE,P_ZIP);

EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error Code = '||SQLCODE); 
DBMS_OUTPUT.PUT_LINE('Error Message = Please check input');     
END EXAM_SP;.   

但是在上面的代码中如果我指出参数它不会工作,不知道怎么做,如果我将OUT参数添加到过程并传递值:

    create or replace 
    PROCEDURE EXAM_SP 
(P_FIRSTNAME IN VARCHAR2,
 P_LASTNAME  IN VARCHAR2,
 P_ADDRESS IN VARCHAR2,
 P_CITY IN VARCHAR2,
 P_STATE IN CHAR,
 P_ZIP IN VARCHAR2,
 P_ERROR OUT VARCHAR2)......

我会收到此错误

ORA-06502: PL/SQL: numeric or value error: character string buffer too small

如果我删除参数并在开头使用原始代码就可以了。

获取该错误的匿名阻止:

DECLARE
  LV_FIRSTNAME_TXT BB_SHOPPER.FIRSTNAME%TYPE := 'FIRST';
  LV_LASTNAME_TXT BB_SHOPPER.LASTNAME%TYPE := 'LAST';
  LV_ADDRESS_TXT BB_SHOPPER.ADDRESS%TYPE := '8899 TAPE PARK';
  LV_CITY_TXT BB_SHOPPER.CITY%TYPE := 'JACKSONVILLE';
  LV_STATE_TXT BB_SHOPPER.STATE%TYPE := 'FLd';
  LV_ZIP_NUMBER BB_SHOPPER.ZIPCODE%TYPE := '34567';
  LV_ERROR varchar2(100);

BEGIN 
  EXAM_SP(LV_FIRSTNAME_TXT, LV_LASTNAME_TXT, LV_ADDRESS_TXT,      
    LV_CITY_TXT,LV_STATE_TXT,LV_ZIP_NUMBER);

    DBMS_OUTPUT.PUT_LINE(LV_ERROR);
END;

步骤:

  create or replace 
  PROCEDURE EXAM_SP 
(P_FIRSTNAME IN VARCHAR2,
 P_LASTNAME  IN VARCHAR2,
 P_ADDRESS IN VARCHAR2,
 P_CITY IN VARCHAR2,
 P_STATE IN CHAR,
 P_ZIP IN VARCHAR2,
 P_ERROR OUT VARCHAR2)



IS  

BEGIN
  INSERT INTO BB_SHOPPER (IDSHOPPER, FIRSTNAME, LASTNAME, ADDRESS, CITY, 
                        STATE, ZIPCODE)
  VALUES                  
         (BB_SHOPPER_IDSHOPPER_SEQ.NEXTVAL,P_FIRSTNAME,P_LASTNAME,P_ADDRESS, 
                        P_CITY, P_STATE,P_ZIP);

EXCEPTION
  WHEN OTHERS THEN
  P_ERROR := SQLCODE;

/*DBMS_OUTPUT.PUT_LINE('Error Code = '||SQLCODE); 
DBMS_OUTPUT.PUT_LINE('Error Message = Please check input'); 
P_ERROR := SQLCODE;*/

END EXAM_SP;

我收到的错误消息:

Error report:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
06502. 00000 -  "PL/SQL: numeric or value error%s"

我已将变量'FLd'传递给过程参数,以便测试错误捕获。

表格结构:

IDSHOPPER   NUMBER(4,0)
FIRSTNAME   VARCHAR2(15 BYTE)
LASTNAME    VARCHAR2(20 BYTE)
ADDRESS VARCHAR2(40 BYTE)
CITY    VARCHAR2(20 BYTE)
STATE   CHAR(2 BYTE)
ZIPCODE VARCHAR2(15 BYTE)
PHONE   VARCHAR2(10 BYTE)
FAX VARCHAR2(10 BYTE)
EMAIL   VARCHAR2(25 BYTE)
USERNAME    VARCHAR2(8 BYTE)
PASSWORD    VARCHAR2(8 BYTE)
COOKIE  NUMBER(4,0)
DTENTERED   DATE
PROVINCE    VARCHAR2(15 BYTE)
COUNTRY VARCHAR2(15 BYTE)
...

2 个答案:

答案 0 :(得分:4)

  

"如果我将参数添加到过程并传递值,我将收到此错误"

亚历克斯提供了很长的答案,我只想提出一个简短的观点:这是不好的做法。大多数编程语言都包含用于处理异常的内置功能。你提出的建议会产生两个架构问题:

  1. 调用程序的程序必须编写非标准代码来捕获错误,这对编写调用程序的开发人员和其他必须了解其工作方式的人员来说都是一种痛苦。
  2. 您的程序不会引发异常,即使它失败了#34;它将成功状态返回给调用程序。如果编写调用程序的开发人员没有实现特殊代码,则异常将丢失,数据库可能会处于无效状态。

答案 1 :(得分:3)

错误堆栈显示:

ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 6
06502. 00000 -  "PL/SQL: numeric or value error%s"

该堆栈显示,部分原因是它没有提及过程名称,错误来自匿名块的第6行。它并没有达到该块第11行的程序调用。

问题是你已经定义了本地状态变量:

LV_STATE_TXT BB_SHOPPER.STATE%TYPE := 'FLd';

使用%TYPE语法,这很棒;但由于表格列为char(2),当您尝试将三字符文本文字'Fld'分配给该双字符本地变量时,它会立即出现错误。

到将三个字符的值发送到过程时,因此insert所期待的错误不会出现,因为insert也不会发生。

如果你想用特定错误打破它,你可以将局部变量声明更改为固定长度,而不是像通常想要的那样使用%TYPE

DECLARE
  LV_FIRSTNAME_TXT BB_SHOPPER.FIRSTNAME%TYPE := 'FIRST';
  LV_LASTNAME_TXT BB_SHOPPER.LASTNAME%TYPE := 'LAST';
  LV_ADDRESS_TXT BB_SHOPPER.ADDRESS%TYPE := '8899 TAPE PARK';
  LV_CITY_TXT BB_SHOPPER.CITY%TYPE := 'JACKSONVILLE';
  --LV_STATE_TXT BB_SHOPPER.STATE%TYPE := 'FLd';
  -- specific length to allow invalid value to be used
  LV_STATE_TXT char(3) := 'FLd';
  LV_ZIP_NUMBER BB_SHOPPER.ZIPCODE%TYPE := '34567';
  LV_ERROR varchar2(100);

BEGIN 
  EXAM_SP(LV_FIRSTNAME_TXT, LV_LASTNAME_TXT, LV_ADDRESS_TXT,      
    LV_CITY_TXT,LV_STATE_TXT,LV_ZIP_NUMBER,LV_ERROR);

 DBMS_OUTPUT.PUT_LINE(LV_ERROR);
END;
/

-12899

PL/SQL procedure successfully completed.

或者更简单地直接为IN参数使用文字,因为此时您只是测试过程:

DECLARE
  LV_ERROR varchar2(100);
BEGIN 
  EXAM_SP('FIRST', 'LAST', '8899 TAPE PARK',      
    'JACKSONVILLE', 'FLd', '34567', LV_ERROR);

 DBMS_OUTPUT.PUT_LINE(LV_ERROR);
END;
/

-12899

PL/SQL procedure successfully completed.

您可能会发现返回错误文本更有用,而不仅仅是数字(如果您使用数字形式参数类型返回错误号,那么就是这样!),例如:

...
EXCEPTION
  WHEN OTHERS THEN
    P_ERROR := SQLERRM;
END EXAM_SP;
/

-- same anonymous block

ORA-12899: value too large for column "MY_SCHEMA"."BB_SHOPPER"."STATE" (actual: 3, maximum: 2)

PL/SQL procedure successfully completed.

当然,正如我在评论中提到的那样,最好让Oracle的异常处理只是将实际的异常冒充到调用方 - 除了记录之外,你应该只抓住异常你实际上可以处理。请注意,传回的错误消息并未告诉您有关 代码中发生错误的任何信息;如果没有异常处理程序,您将在过程中看到具有违规语句的行号。正如APC所指出的那样,每个来电者都必须寻找并处理这些重新定位,这很容易被忽视。当然总有例外(ha),但这似乎是做错事的练习。