JJ.
JJ.

Reputation: 5475

How to determine row/value throwing error in PL/SQL statement?

(Oracle PL/SQL)

If I have a simple SQL statement that is throwing an error, ie:

DECLARE
    v_sql_errm varchar2(2048);
BEGIN
    UPDATE my_table SET my_column = do_something(my_column)
        WHERE my_column IS NOT NULL;
EXCEPTION
    when others then
        -- How can I obtain the row/value causing the error (unknown)?
        v_sql_errm := SQLERRM;
        insert into log_error (msg) values ('Error updating value (unknown): '||
             v_sql_errm);
END;

Is there any way within the exception block to determine the row/value on which the query is encountering an error? I would like to be able to log it so that I can then go in and modify/correct the specific data value causing the error.

Upvotes: 7

Views: 14835

Answers (6)

Rob van Wijk
Rob van Wijk

Reputation: 17705

This can be done using DML error logging, if you are on 10gR2 or later.

An example:

SQL> create table my_table (my_column)
  2  as
  3  select level from dual connect by level <= 9
  4  /

Tabel is aangemaakt.

SQL> create function do_something
  2  ( p_my_column in my_table.my_column%type
  3  ) return my_table.my_column%type
  4  is
  5  begin
  6    return 10 + p_my_column;
  7  end;
  8  /

Functie is aangemaakt.

SQL> alter table my_table add check (my_column not in (12,14))
  2  /

Tabel is gewijzigd.

SQL> exec dbms_errlog.create_error_log('my_table')

PL/SQL-procedure is geslaagd.

This creates an error logging table called err$_my_table. This table is filled by adding a log errors clause to your update statement:

SQL> begin
  2    update my_table
  3       set my_column = do_something(my_column)
  4     where my_column is not null
  5           log errors reject limit unlimited
  6    ;
  7  end;
  8  /

PL/SQL-procedure is geslaagd.

SQL> select * from err$_my_table
  2  /

                       ORA_ERR_NUMBER$
--------------------------------------
ORA_ERR_MESG$
--------------------------------------------------------------------
ORA_ERR_ROWID$
--------------------------------------------------------------------
OR
--
ORA_ERR_TAG$
--------------------------------------------------------------------
MY_COLUMN
--------------------------------------------------------------------
                                  2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAB
U

12

                                  2290
ORA-02290: check constraint (RWK.SYS_C00110133) violated
AAGY/aAAQAABevcAAD
U

14


2 rijen zijn geselecteerd.

Prior to 10gR2, you can use the SAVE EXCEPTIONS clause: http://rwijk.blogspot.com/2007/11/save-exceptions.html

Upvotes: 4

Rob van Wijk
Rob van Wijk

Reputation: 17705

A solution using the SAVE EXCEPTIONS clause:

SQL> create table my_table (my_column)
  2  as
  3  select level from dual connect by level <= 9
  4  /

Table created.

SQL> create function do_something
  2  ( p_my_column in my_table.my_column%type
  3  ) return my_table.my_column%type
  4  is
  5  begin
  6    return 10 + p_my_column;
  7  end;
  8  /

Function created.

SQL> alter table my_table add check (my_column not in (12,14))
  2  /

Table altered.

SQL> declare
  2    e_forall_error exception;
  3    pragma exception_init(e_forall_error,-24381)
  4    ;
  5    type t_my_columns is table of my_table.my_column%type;
  6    a_my_columns t_my_columns := t_my_columns()
  7    ;
  8  begin
  9    select my_column
 10           bulk collect into a_my_columns
 11      from my_table
 12    ;
 13    forall i in 1..a_my_columns.count save exceptions
 14      update my_table
 15         set my_column = do_something(a_my_columns(i))
 16       where my_column = a_my_columns(i)
 17    ;
 18  exception
 19  when e_forall_error then
 20    for i in 1..sql%bulk_exceptions.count
 21    loop
 22      dbms_output.put_line(a_my_columns(sql%bulk_exceptions(i).error_index));
 23    end loop;
 24  end;
 25  /
2
4

PL/SQL procedure successfully completed.

For very large data sets, you probably don't want to blow up your PGA memory, so be sure to use the LIMIT clause in that case.

Upvotes: 2

Manuel Ferreria
Manuel Ferreria

Reputation: 1232

PL/SQL defines 2 global variables to refer to errors:

SQLERRM : SQL error Message

SQLERRNO: SQL error Number

This is readable in the EXCEPTION block in your PL/SQL.

DECLARE 
    x number;
BEGIN
    SELECT 5/0 INTO x FROM DUAL;
EXCEPTION
    WHEN OTHERS THEN:
        dbms_output.put_line('Error Message: '||SQLERRM);
        dbms_output.put_line('Error Number: '||SQLERRNO);
END;

Upvotes: 0

Christian
Christian

Reputation: 7320

Try this (not tested):

    DECLARE
      cursor c1 is
        select key_column, my_column
        from my_table
        WHERE my_column IS NOT NULL
        ORDER BY key_column;

      my_table_rec my_table%ROWTYPE;

    BEGIN

      FOR my_table_rec in c1
      LOOP
        UPDATE my_table SET my_column = do_something(my_column)
            WHERE key_column = my_table_rec.key_column;
      END LOOP;

    EXCEPTION
        when others then
        insert into log_error (msg) values ('Error updating key_column: ' || my_table_rec.key_column || ', my_column: ' || my_table_rec.my_column);
    END;

Upvotes: 0

Adam Paynter
Adam Paynter

Reputation: 46948

For more detailing information regarding how execution arrived at the line in question, you could try displaying the output returned by these functions:

  • DBMS_UTILITY.format_error_stack:

    Format the current error stack. This can be used in exception handlers to look at the full error stack.

  • DBMS_UTILITY.format_error_backtrace:

    Format the backtrace from the point of the current error to the exception handler where the error has been caught. NULL string is returned if no error is currently being handled.

Upvotes: 0

northpole
northpole

Reputation: 10346

try outputting your error and see if it gives you the information you are looking for. For example:

EXCEPTION
    WHEN OTHERS
    THEN
      DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;

Upvotes: 0

Related Questions