识别在 pl sql 块中导致 VALUE_ERROR 的数据的目的地

Identify destination of data which cause VALUE_ERROR in pl sql block

我的包引发 ORA-06502:PL/SQL:数字或值错误。

我可以在 catch 块中添加一些代码来识别哪些数据未成功插入到哪些字段吗?

现在我只有:

WHEN OTHERS
 ...log SQLCODE and SQLERRM...
 RAISE PROGRAM ERROR;
 RETURN;

谢谢。

请多次阅读下面的引述,直到您完全理解为止。

A when others is almost always a BUG unless it is immediately followed by a RAISE.

  • WHEN OTHERS 隐藏了错误的来源。

  • WHEN OTHERS 破坏过程调用的原子性.

记住,对于错误,RAISE –> CATCH –> HANDLE

所以,首先移除所有的异常处理器,然后重新执行PL/SQL块。错误堆栈将包含 正确的行号 和其他错误详细信息。

阅读https://lalitkumarb.wordpress.com/2014/05/02/when-others-then-null-a-bug/

例如,

SQL> CREATE OR REPLACE PROCEDURE p_test_others(i_val IN VARCHAR2) AS
2       o_val NUMBER;
3    BEGIN
4       SELECT i_val INTO o_val FROM dual;
5       DBMS_OUTPUT.PUT_LINE(o_val);
6    EXCEPTION
7       WHEN OTHERS THEN
8          DBMS_OUTPUT.PUT_LINE('SQLCODE: '||SQLCODE);
9          DBMS_OUTPUT.PUT_LINE('Message: '||SQLERRM);
10         RAISE;
11    END;
12    /
Procedure created

SQL>
SQL> BEGIN
2       p_test_others('a');
3    END;
4    /
SQLCODE: -6502
Message: ORA-06502: PL/SQL: numeric or value error: character to number conversion error
BEGIN
p_test_others('a');
END;
ORA-06502: PL/SQL: numeric or value error: character to number conversion error
ORA-06512: at "P_TEST_OTHERS", line 10
ORA-06512: at line 2

否则,

要记录错误,请使用:

  • dbms_utility.format_error_stack,以及
  • dbms_utility.format_error_backtrace

获取调用堆栈。

例如,

SQL> declare
  2    v1 integer := 1;
  3    v2 integer := 0;
  4    v3 integer;
  5    procedure p1 (v1 in integer, v2 in integer, v3 out integer) is 
  6    begin 
  7      v3 := v1 / v2;
  8    end;
  9    procedure p2 (v1 in integer, v2 in integer, v3 out integer) is 
 10    begin 
 11      p1 (v1, v2, v3);
 12    end;
 13  begin
 14    p2 (v1, v2, v3);
 15  exception
 16    when others then
 17      dbms_output.put_line ('---------------------');
 18      dbms_output.put_line ('This is what you record in log table:');
 19      dbms_output.put (dbms_utility.format_error_stack);
 20      dbms_output.put (dbms_utility.format_error_backtrace);
 21      dbms_output.put_line ('---------------------');
 22      raise;
 23  end;
 24  /
---------------------
This is what you record in log table:
ORA-01476: divisor is equal to zero
ORA-06512: at line 7
ORA-06512: at line 11
ORA-06512: at line 14
---------------------
declare
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
ORA-06512: at line 22

更新 根据 OP 的要求

对于异常块,行号将转到异常处理程序的行号。

SQL> BEGIN
  2    INSERT INTO t
  3      (A
  4      ) VALUES
  5      ('one'
  6      );
  7  EXCEPTION
  8  WHEN OTHERS THEN
  9    RAISE;
 10  END;
 11  /
BEGIN
*
ERROR at line 1:
ORA-12899: value too large for column "LALIT"."T"."A" (actual: 3, maximum: 1)
ORA-06512: at line 9


SQL>

没有异常块,你有错误来自的正确行号:

SQL> BEGIN
  2    INSERT INTO t
  3      (A
  4      ) VALUES
  5      ('one'
  6      );
  7  END;
  8  /
BEGIN
*
ERROR at line 1:
ORA-12899: value too large for column "LALIT"."T"."A" (actual: 3, maximum: 1)
ORA-06512: at line 2


SQL>

因此,错误堆栈会告诉您 schametable_namecolumn_name 和其他详细信息,例如允许的最大大小和实际大小。

对于更具体或自定义的错误记录,请在异常块中使用所有必需的列名编写您自己的代码,以记录在这些列中插入值时发生的错误。

假设您遇到这样的情况:

CREATE TABLE MY_TABLE (PK NUMBER PRIMARY KEY, COL_A NUMBER(2), COL_B NUMBER(2));

BEGIN
    INSERT INTO MY_TABLE
    SELECT LEVEL AS PK, ROUND(100*DBMS_RANDOM.NORMAL) AS COL_A, ROUND(100*DBMS_RANDOM.NORMAL) AS COL_B
    FROM dual
    CONNECT BY LEVEL < 20;
EXCEPTION
    WHEN OTHERS THEN 
        DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_STACK);
        DBMS_OUTPUT.PUT_LINE (DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;

ORA-01438: value larger than specified precision allowed for this column
ORA-06512: at line 2

显然您不知道引发错误的行和列。找出它的一种方法是:

CREATE TABLE MY_TABLE_TEMP AS
SELECT LEVEL AS PK, ROUND(100*DBMS_RANDOM.NORMAL) AS COL_A, ROUND(100*DBMS_RANDOM.NORMAL) AS COL_B
FROM dual
CONNECT BY LEVEL < 20;

DECLARE
sqlstr VARCHAR2(1000);
BEGIN

    FOR aRow IN (SELECT * FROM MY_TABLE_TEMP) LOOP
        INSERT INTO MY_TABLE (PK) VALUES (aRow.PK); 
        FOR aCol IN (SELECT * FROM user_tab_cols WHERE table_name = 'MY_TABLE_TEMP') LOOP
        BEGIN
            sqlstr := 'UPDATE MY_TABLE a SET '||aCol.column_name||' = (SELECT '||aCol.column_name||' FROM MY_TABLE_TEMP b WHERE a.PK = b.PK) WHERE a.PK = :pk';
            EXECUTE IMMEDIATE sqlstr USING aRow.PK;
        EXCEPTION
            WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE ( 'Error at line '||aRow.PK||' for column '||aCol.column_name ||' -> '||SQLERRM);
        END;
        END LOOP;
                DBMS_OUTPUT.PUT_LINE ( 'Line '||aRow.PK||' -> OK');
    END LOOP;

END;

Line 1 -> OK
Line 2 -> OK
Error at line 3 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 3 -> OK
Error at line 4 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Line 4 -> OK
Error at line 5 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Line 5 -> OK
Error at line 6 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Line 6 -> OK
Error at line 7 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 7 -> OK
Error at line 8 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Error at line 8 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 8 -> OK
Error at line 9 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Line 9 -> OK
Error at line 10 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 10 -> OK
Line 11 -> OK
Error at line 12 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 12 -> OK
Line 13 -> OK
Line 14 -> OK
Line 15 -> OK
Line 16 -> OK
Error at line 17 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 17 -> OK
Line 18 -> OK
Error at line 19 for column COL_A -> ORA-01438: value larger than specified precision allowed for this column
Error at line 19 for column COL_B -> ORA-01438: value larger than specified precision allowed for this column
Line 19 -> OK