问题
I have PL/SQL table-type and procedure as below. It is just part of code. While populating PL/SQL table type variable I am getting exception.
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;
/
I am getting exception - "ORA-22814: attribute or element value is larger than specified in type". After checking length and data of each and every column, I found that TEST_TABLE.EMPLOYEENAME value is more than 100 chars. Is there a way to print column name for which object population is failing?
Thanks in advance for help
回答1:
You can get a more meaningful error message by creating a custom constructor.
Objects already come with a default constructor that accepts every value and simply assigns them. You can recreate that default constructor with PL/SQL. Then the errors happen in your custom PL/SQL code and the line numbers will be more meaningful.
The code is relatively simple and only affects the type and the type body. None of the code that uses the objects has to change.
For example:
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;
/
Now the program should generate an error message like below.
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?
The error is more obvious now because line 8 is SELF.EMPLOYEENAME := employeename;
.
回答2:
You can if you loop through the rows one-by-one and check each value as you insert it into the object:
SQL Fiddle
Oracle 11g R2 Schema Setup:
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;
/
Query 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
回答3:
Just look at your TEST_TABLE
table for the value that is too long:
SELECT tt.employeename
FROM test_table tt
WHERE LENGTH( tt.employeename ) > 100
A better solution would be to make the employeename
inside your obj_employee
object the same length as the table, so this doesn't happen in the first place. (I can't remember if you can use the %TYPE
operator in an object definition.)
来源:https://stackoverflow.com/questions/49109464/how-to-get-column-name-for-which-pl-sql-object-population-is-failing