【问题标题】:How to get column name for which PL/SQL object population is failing如何获取 PL/SQL 对象填充失败的列名
【发布时间】:2018-03-12 14:45:31
【问题描述】:

我有如下的 PL/SQL 表类型和过程。它只是代码的一部分。在填充 PL/SQL 表类型变量时,我遇到了异常。

CREATE OR REPLACE TYPE OBJ_EMPLOYEE AS OBJECT
(
     EMPID               NUMBER(12)     ,     
     EMPLOYEENAME        VARCHAR2(100)  ,
     /* more attributes */
     STATUS              VARCHAR2(20)   ,
     UPDTDATE            DATE           ,
);

CREATE OR REPLACE TYPE TAB_EMPLOYEE IS TABLE OF OBJ_EMPLOYEE;

CREATE OR REPLACE PROCEDURE sp_getEmpDetails
(
     p_ErrorCode_o       OUT VARCHAR2,
     p_ErrorMsg_o        OUT VARCHAR2
)
IS    
     TEMP_EMPLOYEE    TAB_EMPLOYEE := TAB_EMPLOYEE();    
BEGIN

     /* Some code */

     BEGIN

          SELECT OBJ_EMPLOYE
          (
               EMPID          ,
               EMPLOYEENAME   ,
               /* more attributes */
               STATUS         ,
               UPDTDATE
          )
          BULK COLLECT INTO TEMP_EMPLOYEE
          FROM (
          SELECT    EMPID,
                    EMPLOYEENAME,
                    /* more attributes */
                    STATUS,
                    SYSDATE AS UPDTDATE
          FROM      TEST_TABLE
          );

     EXCEPTION
          WHEN OTHERS THEN
               p_vcErrorCode_o :=  SQLCODE;
               p_vcErrorMsg_o := 'Fail 1' || SQLERRM;

     END;

     /* Some code */

EXCEPTION
     WHEN OTHERS THEN
          p_vcErrorCode_o :=  SQLCODE;
          p_vcErrorMsg_o := SQLERRM;
END sp_getEmpDetails;
/

我遇到异常 - “ORA-22814:属性或元素值大于类型中指定的值”。在检查每一列的长度和数据后,我发现 TEST_TABLE.EMPLOYEENAME 的值超过了 100 个字符。 有没有办法打印对象填充失败的列名?

提前感谢您的帮助

【问题讨论】:

  • 不,没有常规方法可以获取此信息。
  • 不相关的...ISPL ?

标签: oracle plsql oracle11g


【解决方案1】:

您可以通过创建自定义构造函数来获得更有意义的错误消息。

对象已经带有一个默认构造函数,它接受每个值并简单地分配它们。您可以使用 PL/SQL 重新创建该默认构造函数。然后错误发生在您的自定义 PL/SQL 代码中,行号将更有意义。

代码比较简单,只影响类型和类型体。使用对象的任何代码都无需更改。

例如:

CREATE OR REPLACE TYPE OBJ_EMPLOYEE AS OBJECT
(
     EMPID               NUMBER(12)     ,     
     EMPLOYEENAME        VARCHAR2(100)  ,
     /* more attributes */
     STATUS              VARCHAR2(20)   ,
     UPDTDATE            DATE           ,
     CONSTRUCTOR FUNCTION OBJ_EMPLOYEE
          (EMPID NUMBER, EMPLOYEENAME VARCHAR2, STATUS VARCHAR2, UPDTDATE DATE)
          RETURN SELF AS RESULT
);


CREATE OR REPLACE TYPE BODY OBJ_EMPLOYEE AS
     --Recreate default constructor to generate more useful error message.
     CONSTRUCTOR FUNCTION OBJ_EMPLOYEE
          (EMPID NUMBER, EMPLOYEENAME VARCHAR2, STATUS VARCHAR2, UPDTDATE DATE)
          RETURN SELF AS RESULT AS
     BEGIN
          SELF.EMPID := empid;
          SELF.EMPLOYEENAME := employeename;
          SELF.STATUS := status;
          SELF.UPDTDATE := updtdate;
          RETURN;
     END;
END;
/

现在程序应该生成如下所示的错误消息。

ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "JHELLER.OBJ_EMPLOYEE", line 8
ORA-06512: at line 17

View program sources of error stack?

现在错误更明显了,因为第 8 行是SELF.EMPLOYEENAME := employeename;。

【讨论】:

  • 谢谢。它解决了我的问题。希望添加默认构造函数不会增加额外的时间/任何未知问题。好吧,需要更多地阅读默认构造函数。谢谢:-)
【解决方案2】:

如果您逐个循环遍历行并在将每个值插入对象时检查每个值,则可以:

SQL Fiddle

Oracle 11g R2 架构设置:

CREATE TABLE TEST_TABLE ( EMPID, EMPLOYEENAME, STATUS ) AS
SELECT 1, 'A',                   'X' FROM DUAL UNION ALL
SELECT 2, RPAD( 'B', 101, 'B' ), 'Y' FROM DUAL UNION ALL
SELECT 3, 'C',                   'Z' FROM DUAL
/


CREATE OR REPLACE TYPE OBJ_EMPLOYEE AS OBJECT
(
     EMPID               NUMBER(12)     ,     
     EMPLOYEENAME        VARCHAR2(100)  ,
     STATUS              VARCHAR2(20)   ,
     UPDTDATE            DATE
)
/

CREATE OR REPLACE TYPE TAB_EMPLOYEE IS TABLE OF OBJ_EMPLOYEE
/


CREATE OR REPLACE PROCEDURE sp_getEmpDetails
(
  p_vcErrorCode_o       OUT VARCHAR2,
  p_vcErrorMsg_o        OUT VARCHAR2
)
IS
  TEMP_EMPLOYEE    OBJ_EMPLOYEE;
  TEMP_EMPLOYEES   TAB_EMPLOYEE := TAB_EMPLOYEE();    
BEGIN
  FOR rec IN ( SELECT * FROM TEST_TABLE ) LOOP
    TEMP_EMPLOYEE := OBJ_EMPLOYEE( NULL, NULL, NULL, SYSDATE );
    BEGIN
      TEMP_EMPLOYEE.EMPID := rec.EMPID;
    EXCEPTION
      WHEN OTHERS THEN
        p_vcErrorCode_o :=  SQLCODE;
        p_vcErrorMsg_o := 'Fail on EMPID: ' || SQLERRM;
    END;

    BEGIN
      TEMP_EMPLOYEE.EMPLOYEENAME := rec.EMPLOYEENAME;
    EXCEPTION
      WHEN OTHERS THEN
        p_vcErrorCode_o :=  SQLCODE;
        p_vcErrorMsg_o := 'Fail on EMPLOYEENAME: ' || SQLERRM;
    END;

    BEGIN
      TEMP_EMPLOYEE.STATUS := rec.STATUS;
    EXCEPTION
      WHEN OTHERS THEN
        p_vcErrorCode_o :=  SQLCODE;
        p_vcErrorMsg_o := 'Fail on STATUS: ' || SQLERRM;
    END;

    TEMP_EMPLOYEES.EXTEND();
    TEMP_EMPLOYEES( TEMP_EMPLOYEES.COUNT ) := TEMP_EMPLOYEE;
  END LOOP;
EXCEPTION
  WHEN OTHERS THEN
    p_vcErrorCode_o :=  SQLCODE;
    p_vcErrorMsg_o := 'Fail 1' || SQLERRM;
END sp_getEmpDetails;
/

查询 1:

DECLARE
  code NUMBER(6,0);
  errm VARCHAR2(4000);
BEGIN
  sp_getEmpDetails( code, errm );
  DBMS_OUTPUT.PUT_LINE( code || ' - ' || errm );
END;

Results:

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

【讨论】:

  • 是的。这只是一种编程方式,而不是手动调试。但是我在单个 Pl/SQL 对象中有很多属性,而且我有很多这样的对象;所以添加此代码不会给我带来 BULK COLLECT 操作的好处。
【解决方案3】:

只需查看您的 TEST_TABLE 表中的值是否太长:

SELECT tt.employeename
FROM test_table tt
WHERE LENGTH( tt.employeename ) > 100

更好的解决方案是使employeename 在您的obj_employee 对象中的长度与表格的长度相同,这样一开始就不会发生这种情况。 (我不记得你是否可以在对象定义中使用%TYPE 运算符。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-20
    相关资源
    最近更新 更多