Skip to Main Content
  • Questions
  • capturing business logic validation errors using log_errors

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question.

Asked: January 03, 2008 - 6:17 pm UTC

Last updated: June 17, 2011 - 1:42 pm UTC

Version: 10.2.0

Viewed 1000+ times

You Asked

Hi Tom,

I have some data that I need to validate with business logic while moving it from a set of tables to a single table. I'm able to use an insert as select statement to get the data from the set of tables to the single table, and I want to have records that have fields that fail our validation checks (or have any Oracle errors) to be automatically saved off to an error table. I'm trying to use log_errors for this, but the only way I can get it to work is by having the business logic checks in a trigger, which is slowing down the processing tremendously.

Our business logic checks are in functions something like this:
function translate_field (p_number in number) return varchar2 is
begin
  if p_number between 1 and 5 then
    return 'A';
  elsif p_number between 6 and 9 then
    return 'B';
  else
    raise_application_error(-20001, 'Unrecognized number passed in: ' || p_number);
end translate_field;

Not all of our business logic checks are exactly like this, but we are using raise_application_error at the end of all of them to indicate that some sort of validation failed. Ideally I'd like to be able to do the following:
insert into new_table
select col1, translate_field(col2)
from   old_table
log_errors into err$_new_table reject limit unlimited;

But that just causes the insert to fail when translate_field throws an exception. What I've currently got it set up to do is just select the raw values in the insert as select statement, and then have a trigger run the validation functions on each column we want to validate. This works ok except that it's extremely slow. I'd prefer not to have to use a trigger, but I'm having a really hard time coming up with an alternative.

Do you have any suggestions?

Thanks in advance for your help.

and Tom said...

well, you can use a multi-table insert with a single insert - not sure if it will be significantly faster (you can test in your environment and let us know :) )

ops$tkyte%ORA10GR2> create table t ( x int, y varchar2(5) );

Table created.

ops$tkyte%ORA10GR2> exec dbms_errlog.create_error_log( 'T' );

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> create or replace function
  2  translate_field (p_number in number) return varchar2
  3  is
  4  begin
  5    if p_number between 1 and 5 then
  6      return 'A';
  7    elsif p_number between 6 and 9 then
  8      return 'B';
  9    else
 10      raise_application_error(-20001, 'Unrecognized number passed in: ' || p_number);
 11    end if;
 12  end translate_field;
 13  /

Function created.

ops$tkyte%ORA10GR2>

ops$tkyte%ORA10GR2> insert ALL
  2  into t ( x, y )
  3  values ( a, translate_field( b ) )
  4  log errors reject limit unlimited
  5  select rownum a, mod(rownum,10)+1 b
  6    from all_objects;

44829 rows created.

Rating

  (21 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

What value does a multi table insert add here ?

Vinay, January 04, 2008 - 5:13 am UTC

Hi Tom,

Just curious to know whats the benefit of using the multi table insert here. I tried the multi table inserts as well as standard inserts with the same results in the err$_t table.


scott@ORA11GLI> insert ALL
  2  into t ( x, y )
  3  values ( a, translate_field( b ) )
  4  log errors reject limit unlimited
  5  select rownum a, mod(rownum,10)+1 b
  6    from all_objects;

48520 rows created.

scott@ORA11GLI> select count(*) from err$_t;

  COUNT(*)
----------
      5391

scott@ORA11GLI> truncate table err$_t;

Table truncated.

scott@ORA11GLI> truncate table t;

Table truncated.

scott@ORA11GLI> insert into t ( x, y )
  2  select rownum, translate_field(mod(rownum,10)+1)
  3    from all_objects
  4  log errors reject limit unlimited ;

48520 rows created.

scott@ORA11GLI> select count(*) from err$_t;

  COUNT(*)
----------
      5391



Regards
Vinay
Tom Kyte
January 04, 2008 - 11:52 am UTC

indeed, I was taking the poster at their word - they claimed to not be getting the rows recorded the other way.

.... but the only way I can get it to work is by having the business logic checks in a trigger, .....


That was tested on Oracle 11g

Vinay, January 04, 2008 - 6:24 am UTC

Forgot to mention, I tested this on Oracle 11g:


Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options



Regards
Vinay
Tom Kyte
January 04, 2008 - 12:05 pm UTC

I just tested in 10g - results are the same. given their example, the multi-table insert would be not necessary, I have to presume their real life issue is different from what they posted - thanks!

ops$tkyte%ORA10GR2> select count(*) from err$_t;

  COUNT(*)
----------
         0

ops$tkyte%ORA10GR2> insert into t
  2  select rownum, translate_field( mod(rownum,10)+1 )
  3    from all_users
  4   log errors reject limit unlimited;

53 rows created.

ops$tkyte%ORA10GR2> select count(*) from err$_t;

  COUNT(*)
----------
         6


Adding DISTINCT duplicates my problem

A reader, January 07, 2008 - 9:43 am UTC

Thank you for your repsonse. I copied and pasted your example, and it worked for me too, so I was pretty confused at first.

I didn't realize it at the time that I posted the original question, but it turns out the having DISTINCT in the select statement is what is causing the problem. I need the DISTINCT to keep from getting duplicate rows from my query that does a join over 4 or 5 tables (although I'm guessing there may be a better way to do the join that doesn't require DISTINCT).

Does it make sense to you that adding DISTINCT to the select statement would change the behavior of log errors?

  SQL>  insert into t
  2  select distinct rownum, translate_field( mod(rownum,10)+1 )
  3  from all_users
  4* log errors reject limit unlimited
select distinct rownum, translate_field( mod(rownum,10)+1 )
                        *
ERROR at line 2:
ORA-20001: Unrecognized number passed in: 10
ORA-06512: at "NXEM.TRANSLATE_FIELD", line 10


Thanks again.

Tom Kyte
January 07, 2008 - 11:25 am UTC

yeah, because the entire query fails now - not just rows of the query. we cannot distinct those failures.


Alberto Dell'Era, January 07, 2008 - 5:57 pm UTC

But isn't
SQL> insert into t ( x, y )
  2  select rownum, translate_field(mod(rownum,10)+1)
  3    from all_objects
  4  log errors reject limit unlimited ;

a tad weak in general (without rownum), since one cannot guarantee that translate_field() is calculated at row insert time - since the kernel could decide, say, to push translate_field() inside the select statement - or, say, materialize the result set before inserting "in bulk" [or, say again, partially materialize up to a block worth of rows, which would look like a nice optimization latch-wise ] ?

Actually, the OP already experienced something like this when S/He used "translate_field(col2)" and, probably, translate_field() was pushed "next to the source table old_table" - passing a dummy rownum "translate_field(col2, rownum)" could prevent the kernel from pushing, but not materializing.

Or is it documented somewhere that this is not the case for "log errors" statements ?

In constrast,
SQL> insert ALL
  2  into t ( x, y )
  3  values ( a, translate_field( b ) )
  4  log errors reject limit unlimited
  5  select rownum a, mod(rownum,10)+1 b
  6    from all_objects;

seems to convey much better the coder's intention to calculate translate_field() for each row, just before inserting - even if it would be nice to know whether the kernel is documented to be bound to honor this intention (and it is not free e.g. to rewrite the latter statement as the former one).

Another way of capturing the business logic validation errors

Matt Brown, January 08, 2008 - 12:40 pm UTC

For the example that was provided, instead of using the function to do the translation / BR check, wouldn't it be cleaner to do the field translation via a case statement and keep the field validation as a check constraint?

CREATE TABLE T
(
X INTEGER NULL,
Y VARCHAR2(5 BYTE) NULL
);


ALTER TABLE T ADD (
CONSTRAINT TEST_BR
CHECK (x between 1 and 9));

exec dbms_errlog.create_error_log( 'T' );

insert /*+ append */into t ( x, y )
select mod(rownum,10)+1,
case
when mod(rownum,10)+1 between 1 and 5
then 'A'
when mod(rownum,10)+1 between 6 and 9
then 'B'
else 'C'
end as b
from all_objects
log errors reject limit unlimited;

I'm not sure how sophisticated the check constriants can be before they start slowing down the insert statement, or how sophisticated you need them to be to support you Business Logic, but at least this way you write less code, and can hopefully do a direct path load.

10gR2 - LOG ERRORS behaves unexpectedly with parallel dml enabled

ibrahim Sahinoglu, January 11, 2008 - 8:33 am UTC

Hello Tom;

In the test case below, errors are logged when parallel dml is disabled, but when parallel dml is enabled, the execution breaks. Any thing I am missing here?

Thanks
create table table_01
as
select t1.OBJECT_ID, t1.OBJECT_NAME
from all_objects t1
where 1 = 2;

create table table_02
as
select t1.OBJECT_ID, t1.OBJECT_NAME
from all_objects t1
where rownum < 11;

-- The below stament will cause invalid number error.
alter table table_01 modify (object_name number);

begin DBMS_ERRLOG.CREATE_ERROR_LOG('table_01'); end;

insert /*+ APPEND PARALLEL(p) */ into table_01 p
select /*+ PARALLEL(t1) */  t1.OBJECT_ID, t1.OBJECT_NAME
from table_02 t1
log errors into err$_table_01('aaa') REJECT LIMIT UNLIMITED;

-- 0 rows created.

select * from err$_table_01 ;

-- 10 rows selected.

truncate table err$_table_01 ;

-- Table truncated.

alter session enable parallel dml;

-- Session altered.

-- Here comes the error. Should have only logged to error table. 
-- Instead puts some log on table and interrupts the operation.

insert /*+ APPEND PARALLEL(p) */ into table_01 p
select /*+ PARALLEL(t1) */  t1.OBJECT_ID, t1.OBJECT_NAME
from table_02 t1
log errors into err$_table_01('aaa') REJECT LIMIT UNLIMITED;

ERROR at line 1:
ORA-12801: error signaled in parallel query server P002
ORA-01722: invalid number




error logging

A reader, January 18, 2008 - 10:51 am UTC

And the underlying data itself is sometimes wrong when error logging is used with updates - no error is thrown though

So we are using only error logging with inserts, not updates

Capturing

Stuart, February 18, 2009 - 7:40 pm UTC

Hi Tom,

Is there any way of capturing all the check constraint violations at once? I was hoping DBMS_ERRLOG would do it, but it doesn't.

For example, in the below SQL, I'm deliberately violating two constraints. Is there a way to report both of them at once, or am I doomed to iterations of find the first violation, fixing, then running again to see if any more check constraints violated?...

10gR2: SCOTT > !cat test.sql
create table error_test
( WAVE_NUM NUMBER DEFAULT 1,
REG_NUM NUMBER,
FISH NUMBER CONSTRAINT FISH_CHK
CHECK ( FISH IN ( 1,2,3,4,5 )),
RED_MEAT NUMBER CONSTRAINT RED_MEAT_CHK
CHECK ( RED_MEAT IN ( 1,2,3,4,5 )),
CHICKEN NUMBER CONSTRAINT CHICKEN_1_CHK
CHECK ( CHICKEN IN ( 1,2,3,4,5 ))
);

begin
dbms_errlog.create_error_log('ERROR_TEST','ERR$_ERROR_TEST') ;
end;
/

insert into error_test
( reg_num, fish, red_meat, chicken )
values
( 1,88,61,1 )
log errors into ERR$_ERROR_TEST reject limit unlimited;

commit;

10gR2: SCOTT > @test

Table created.


PL/SQL procedure successfully completed.


0 rows created.


Commit complete.


ORA_ERR_NUMBER$ ORA_ERR_MESG$ REG_NUM
--------------- ---------------------------------------------------------------------- ----------
2290 ORA-02290: check constraint (SCOTT.FISH_CHK) violated 1

1 row selected.

... but what I'd really like is:

ORA_ERR_NUMBER$ ORA_ERR_MESG$ REG_NUM
--------------- ---------------------------------------------------------------------- ----------
2290 ORA-02290: check constraint (SCOTT.FISH_CHK) violated 1
2290 ORA-02290: check constraint (SCOTT.RED_MEAT_CHK) violated 1

2 rows selected.

Tom Kyte
February 19, 2009 - 7:21 am UTC

it short circuits - once you hit an error, it is an error - done.


You could set the constraints deferrable and check them yourself, but you'd have to do it row by row (slow by slow), it wouldn't be very useful

ops$tkyte%ORA10GR2> create table error_test
  2  (   WAVE_NUM                  NUMBER     DEFAULT 1,
  3      REG_NUM                   NUMBER,
  4      FISH                      NUMBER     CONSTRAINT  FISH_CHK
  5                                               CHECK ( FISH                    IN ( 1,2,3,4,5 )) deferrable,
  6      RED_MEAT                  NUMBER     CONSTRAINT  RED_MEAT_CHK
  7                                               CHECK ( RED_MEAT                IN ( 1,2,3,4,5 )) deferrable,
  8      CHICKEN                   NUMBER     CONSTRAINT  CHICKEN_1_CHK
  9                                               CHECK ( CHICKEN                 IN ( 1,2,3,4,5 ))  deferrable
 10  );

Table created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> insert into error_test
  2  ( reg_num, fish, red_meat, chicken )
  3  values
  4    ( 1,88,61,1 )
  5  /
insert into error_test
*
ERROR at line 1:
ORA-02290: check constraint (OPS$TKYTE.RED_MEAT_CHK) violated


ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> set constraints all deferred;

Constraint set.

ops$tkyte%ORA10GR2> insert into error_test
  2  ( reg_num, fish, red_meat, chicken )
  3  values
  4    ( 1,88,61,1 )
  5  /

1 row created.

ops$tkyte%ORA10GR2> set constraint FISH_CHK immediate;
set constraint FISH_CHK immediate
*
ERROR at line 1:
ORA-02290: check constraint (OPS$TKYTE.FISH_CHK) violated


ops$tkyte%ORA10GR2> set constraint red_meat_chk immediate;
set constraint red_meat_chk immediate
*
ERROR at line 1:
ORA-02290: check constraint (OPS$TKYTE.RED_MEAT_CHK) violated


ops$tkyte%ORA10GR2> set constraint chicken_1_chk immediate;

Constraint set.

Capturing Multiple Check Constraints

Stuart, February 19, 2009 - 3:15 pm UTC

Hi Tom,

Thanks for your response. I agree that setting deferrable constraints would be a painful way of doing it.

I will either write a stored procedure or create a view to show the integrity violations. Unfortunately, both will require some coding, and will be rather ugly.

Another forum thread has some interesting feedback on complex constraints which I will also consider, although I think I'd run into the same issue with multiple check constraints not being reported if I used a Materilized view.
http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:21389386132607

I look forward to the day when Oracle release new functionality around check constraints.

Can't you use a multi-table insert for check constraints?

James, May 24, 2009 - 1:07 pm UTC

Can't we use a multi-table insert?


INSERT FIRST 
WHEN (CHECK1 NOT IN (x,y,z) AND 
CHECK2 NOT IN (1,2,3) ) THEN INTO table1
(...) ELSE ....


It could be clunky with a lot of criteria, but it would allow you to check multiple constraints simultaneously without going row by row.

Any drawbacks that make this approach unworkable from a performance (or other) perspective?
Tom Kyte
May 26, 2009 - 8:42 am UTC

why wouldn't you have them on the table ?!?!?!?!?!?!?!

there will be other DML done on this data some day (if not already), you do not put your integrity constraints into your code, you put them on the entities.


integrity constraints go on tables.

integrity constraints do not go in code.

ORA-01031: insufficient privileges.

Rajeshwaran, Jeyabal, August 25, 2009 - 8:05 am UTC

 Scott@IRADSDB> DESC T;
  Name                                                                    Null?    Type
  ----------------------------------------------------------------------- -------- ---------
  X                                                                       NOT NULL NUMBER

 Scott@IRADSDB> CREATE OR REPLACE PACKAGE PKG1 AS
   2    PROCEDURE CREATE_ERROR_LOG;
   3    PROCEDURE DROP_ERROR;
   4  END;
   5  /

 Package created.

 Scott@IRADSDB> CREATE OR REPLACE PACKAGE BODY PKG1 AS
   2    PROCEDURE  CREATE_ERROR_LOG AS
   3    BEGIN
   4      dbms_errlog.create_error_log(dml_table_name=>'T');    <<<========
   5    END CREATE_ERROR_LOG;  
   6    
   7    PROCEDURE DROP_ERROR AS
   8    BEGIN
   9      EXECUTE IMMEDIATE ' DROP TABLE ERR$_T PURGE ';
  10    END;  
  11  END;  
  12  /

 Package body created.

 Scott@IRADSDB> exec pkg1.CREATE_ERROR_LOG;
 BEGIN pkg1.CREATE_ERROR_LOG; END;

 *
 ERROR at line 1:
 ORA-01031: insufficient privileges
 ORA-06512: at "SYS.DBMS_ERRLOG", line 258
 ORA-06512: at "SCOTT.PKG1", line 4
 ORA-06512: at line 1

 Scott@IRADSDB> begin
   2  dbms_errlog.create_error_log(dml_table_name=>'T');     <<<=========
   3  end;
   4  /

 PL/SQL procedure successfully completed.

 Scott@IRADSDB> desc err$_T;
  Name                                                                    Null?    Type
  ----------------------------------------------------------------------- -------- ----------------
  ORA_ERR_NUMBER$                                                                  NUMBER
  ORA_ERR_MESG$                                                                    VARCHAR2(2000)
  ORA_ERR_ROWID$                                                                   ROWID
  ORA_ERR_OPTYP$                                                                   VARCHAR2(2)
  ORA_ERR_TAG$                                                                     VARCHAR2(2000)
  X                                                                                VARCHAR2(4000)

Tom,
The user SCOTT has the DBA privilege Granted to it.
While calling procedure DBMS_ERRLOG.CREATE_ERROR_LOG from Anonymous Blocks it is executed sucessfully.
But while Calling the procedure DBMS_ERRLOG.CREATE_ERROR_LOG from a package PKG1 i am getting the error ORA-01031: insufficient privileges.
please help me how to resolve this also why i am getting this error when invoking from packages.

ORA-01031 : Disappearing Privilege Disorder

Duke Ganote, August 25, 2009 - 1:59 pm UTC

Curious timing! I was just helping one of our developers with this issue yesterday, and wrote this:
http://it.toolbox.com/blogs/data-ruminations/ora01031-disappearing-privilege-disorder-33707

roles

A reader, August 25, 2009 - 7:23 pm UTC


Error Logging in IOT.

Rajeshwaran, Jeyabal, January 05, 2010 - 10:27 pm UTC

rajesh@IRADSDB> CREATE TABLE t_iot
  2    (
  3      X NUMBER PRIMARY KEY
  4    )
  5    organization INDEX;

Table created.

rajesh@IRADSDB> INSERT INTO t_iot
  2  SELECT level
  3  FROM dual
  4  CONNECT BY level <=5
  5  /

5 rows created.

rajesh@IRADSDB> commit;

Commit complete.

rajesh@IRADSDB> exec dbms_errlog.create_error_log(dml_table_name=>'T_IOT');

PL/SQL procedure successfully completed.

rajesh@IRADSDB> INSERT INTO t_iot
  2  SELECT level
  3  FROM dual
  4  CONNECT BY level <=5
  5  LOG ERRORS INTO ERR$_T_IOT REJECT LIMIT UNLIMITED;
INSERT INTO t_iot
*
ERROR at line 1:
ORA-00001: unique constraint (RAJESH.SYS_IOT_TOP_111191) violated

Tom,
Does IOT's doesnot support DML Error logging feature in 10g (Version Oracle 10gR2)? i verified the product docs. but no restrictions.
Can you plese help me.
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/statements_9014.htm#i2080505
Tom Kyte
January 06, 2010 - 8:09 am UTC

ops$tkyte%ORA10GR2> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - Prod
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0      Production
TNS for Linux: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

ops$tkyte%ORA10GR2> create table t ( x number primary key ) organization index;

Table created.

ops$tkyte%ORA10GR2> insert into t select level from dual connect by level <= 5;

5 rows created.

ops$tkyte%ORA10GR2> commit;

Commit complete.

ops$tkyte%ORA10GR2> exec dbms_errlog.create_error_log( dml_table_name => 'T' );

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> insert into t select level from dual connect by level <= 5
  2  log errors reject limit unlimited;

0 rows created.



I cannot reproduce, can you run my testcase as it exists and report back?

drop table t;
drop table err$_t;
select * from v$version;
create table t ( x number primary key ) organization index;
insert into t select level from dual connect by level <= 5;
commit;
exec dbms_errlog.create_error_log( dml_table_name => 'T' );
insert into t select level from dual connect by level <= 5
log errors reject limit unlimited;

Error Logging in IOT.

Rajeshwaran, Jeyabal, January 06, 2010 - 8:52 am UTC

Tom,

Here is my results. I think the problem is with 10.2.0.1 version. Please correct me if i am wrong.

rajesh@IRADSDB> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Prod
PL/SQL Release 10.2.0.1.0 - Production
CORE    10.2.0.1.0      Production
TNS for 32-bit Windows: Version 10.2.0.1.0 - Production
NLSRTL Version 10.2.0.1.0 - Production

rajesh@IRADSDB> create table t ( x number primary key ) organization index;

Table created.

rajesh@IRADSDB> insert into t select level from dual connect by level <= 5;

5 rows created.

rajesh@IRADSDB> commit;

Commit complete.

rajesh@IRADSDB> exec dbms_errlog.create_error_log( dml_table_name => 'T' );

PL/SQL procedure successfully completed.

rajesh@IRADSDB> insert into t select level from dual connect by level <= 5
  2  log errors reject limit unlimited;
insert into t select level from dual connect by level <= 5
*
ERROR at line 1:
ORA-00001: unique constraint (RAJESH.SYS_IOT_TOP_111216) violated

scott@10G> select * from v$version;

BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.3.0 - Prod
PL/SQL Release 10.2.0.3.0 - Production
CORE    10.2.0.3.0      Production
TNS for 32-bit Windows: Version 10.2.0.3.0 - Production
NLSRTL Version 10.2.0.3.0 - Production

scott@10G> create table t ( x number primary key ) organization index;

Table created.

scott@10G> insert into t select level from dual connect by level <= 5;

5 rows created.

scott@10G> commit;

Commit complete.

scott@10G> exec dbms_errlog.create_error_log( dml_table_name => 'T' );

PL/SQL procedure successfully completed.

scott@10G> insert into t select level from dual connect by level <= 5
  2  log errors reject limit unlimited;
insert into t select level from dual connect by level <= 5
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.SYS_IOT_TOP_62719) violated


Tom Kyte
January 06, 2010 - 1:10 pm UTC

yes, reproduced in 10.2.0.1 - looks like it was a bug fixed in 10.2.0.4

Error Logging in IOT.

Rajeshwaran, Jeyabal, January 10, 2010 - 10:31 pm UTC

ir@IR> select * from v$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE    10.2.0.4.0      Production
TNS for Solaris: Version 10.2.0.4.0 - Production
NLSRTL Version 10.2.0.4.0 - Production

5 rows selected.

ir@IR>      CREATE TABLE ADT_IOT
  2        (
  3          CK      VARCHAR2(1000) CONSTRAINT adt_dx_iot PRIMARY KEY,
  4          EK     NUMBER(10),
  5          DK    NUMBER(10),
  6          HD      VARCHAR2(10),
  7          CT      DATE
  8        ) ORGANIZATION INDEX OVERFLOW TABLESPACE TS_AUDIT;

ir@IR> EXEC dbms_errlog.create_error_log(dml_table_name=>'ADT_IOT',err_log_table_space=>'TS_AUDIT');

ir@IR>  INSERT INTO ADT_IOT
  2        (
  3         CK    ,
  4         EK    ,
  5         DK    ,
  6         HD    ,
  7         CT
  8        )
  9        
 10         SELECT
 11         NVL2(ENC.HD,ENC.HD||'_',ENC.HD)           ||
 12         NVL2(ENC.DK,ds.source_cd||'_',ENC.DK) ||
 13         NVL2(ENC.LK,lb.LD||'_','NA_')           ||
 14         NVL2(ENC.MN,ENC.MN||'_','NA_')         ||
 15         NVL2(ENC.MD,ENC.MD||'_','NA_')       ||
 16         ENC.ER                 ||'_'||
 17         dcd.dd                  AS CK      ,
 18         ENC.EK,
 19         aud.DK ,
 20         ENC.HD,
 21         SYSDATE
 22        FROM T1 AUD       ,
 23          T2 ENC        ,
 24          T3  ds      ,
 25          T4 lb     ,
 26          T5 dcd
 27        WHERE ENC.EK = AUD.EK
 28        AND   ENC.MAX_DT BETWEEN TO_DATE('01/01/2009','MM/DD/YYYY') AND TO_DATE('12/31/2009','MM/DD/YYYY')
 29        AND   ENC.DK      = ds.SK (+)
 30        AND   ENC.LK       = lb.LOB_KEY(+)
 31        AND   dcd.diag_cd_key      = aud.DK
 32        LOG ERRORS  REJECT LIMIT UNLIMITED;
  INSERT INTO ADT_IOT
*
ERROR at line 1:
ORA-38906: insert into DML Error Logging table "ERR$_ADT_IOT" failed
ORA-00001: unique constraint (IRADS.ADT_DX_IOT) violated


Tom,
please correct me what i am doing wrong. Why DML Error logging in table "ERR$_ADT_IOT" failed ?
Tom Kyte
January 18, 2010 - 6:08 am UTC

what is the output of

select dbms_metadata.get_ddl( 'TABLE', 'ERR$_ADT_IOT' ) from dual;



and verify there are no triggers on err$_adt_iot and that the tablespace has room for this table to grow.

Error Logging in IOT.

Rajeshwaran, Jeyabal, January 18, 2010 - 10:42 am UTC

irads@IRADSDB> select dbms_metadata.get_ddl( 'TABLE', 'ERR$_ADT_IOT' ) from dual; 
  CREATE TABLE "IRADS"."ERR$_ADT_IOT" 
   ( "ORA_ERR_NUMBER$" NUMBER, 
  "ORA_ERR_MESG$" VARCHAR2(2000), 
  "ORA_ERR_ROWID$" UROWID (4000), 
  "ORA_ERR_OPTYP$" VARCHAR2(2), 
  "ORA_ERR_TAG$" VARCHAR2(2000), 
  "CK" VARCHAR2(4000), 
  "EK" VARCHAR2(4000), 
  "DK" VARCHAR2(4000), 
  "HD" VARCHAR2(4000), 
  "CT" VARCHAR2(4000)
   ) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
  PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT)
  TABLESPACE "TS_AUDIT";
 
irads@IRADSDB> SELECT * FROM user_dependencies
  2  WHERE referenced_name ='ERR$_ADT_IOT'
  3  AND type ='TRIGGER';

no rows selected


Tom,
Also the tablespace TS_AUDIT has more than 122 GB Freespace.
Tom Kyte
January 18, 2010 - 5:45 pm UTC

i see nothing obvious

turn on sql_trace and see what you see in the trace file, look for #err and see if anything pops out.

ERRLOG in a remote database

YG, February 26, 2010 - 1:22 pm UTC

Hello,
I try to use errlog in a remote database and I get error.
It works fine in local database but failes in remote.

Here's my test case:
-- Local database
create table t(n number primary key);

Table created.

Elapsed: 00:00:00.32
exec dbms_errlog.create_error_log('t','t_errlog')

PL/SQL procedure successfully completed.

Insert the 1st row - works.

insert into t values(1)
log errors into t_errlog('test') reject limit 10;

1 row created.

Insert the same row - 0 rows inserted, error goes into errlog.

insert into t values(1)
log errors into t_errlog('test') reject limit 10;

0 rows created.

11:08:27 AP2> select ORA_ERR_NUMBER$,ORA_ERR_OPTYP$,ORA_ERR_TAG$,N,ORA_ERR_MESG$
11:08:52   2  from t_errlog
11:08:57   3  /

ORA_ERR_NUMBER$ OR ORA_ERR_TA N          ORA_ERR_MESG$ 
--------------- -- ---------- ---------- ------------------
              1 I  test       1          ORA-00001: unique constraint (VZWOLAP.SYS_C0028380) violated

1 row selected.

rollback;
Rollback complete.

-- Remote database
I can see errlog in remote database
desc vzwolap.t_errlog@tp_2_ap
 Name                    Null?    Type
 ----------------------- -------- ----------------
 ORA_ERR_NUMBER$                  NUMBER
 ORA_ERR_MESG$                    VARCHAR2(2000)
 ORA_ERR_ROWID$                   ROWID
 ORA_ERR_OPTYP$                   VARCHAR2(2)
 ORA_ERR_TAG$                     VARCHAR2(2000)
 N                                VARCHAR2(4000)

I can manually insert into remote errlog:
insert into vzwolap.t_errlog@tp_2_ap values(1,'aaa',null,'I','test','xxxx');

1 row created.

When I try to insert the 1st row it works:

insert into t@tp_2_ap values(1)
log errors into t_errlog@tp_2_ap('test') reject limit 10;

1 row created.

When I try to insert the same row for the 2nd time, it fails:
insert into t@tp_2_ap values(1)
log errors into t_errlog@tp_2_ap('test') reject limit 10;

insert into t@tp_2_ap values(1)
*
ERROR at line 1:
ORA-00001: unique constraint
(VZWOLAP.SYS_C0028380) violated
ORA-02063: preceding line from TP_2_AP

Tom Kyte
March 01, 2010 - 11:34 am UTC

see Note 1058020.1, I put in a note to get the docs updated to reflect that.

INSERT....LOG ERRORS

Robert, March 30, 2010 - 1:42 pm UTC

Tom,

Is there a way to know if any rows were created into the ERR$_ table after an insert?

e.g.

insert into x (select .... from ...)
log errors reject limit unlimited
/

[ then check some return code here? ]

Thanks,

Robert.
Tom Kyte
April 05, 2010 - 11:06 am UTC

you would query the error log table to determine that. You would use a tag on the insert to uniquely identify YOUR rows


ops$tkyte%ORA10GR2> create table t ( x int check (x>0) );

Table created.

ops$tkyte%ORA10GR2> exec dbms_errlog.create_error_log('T');

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> create sequence error_log_seq;

Sequence created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> desc err$_t;
 Name                                     Null?    Type
 ---------------------------------------- -------- ----------------------------
 ORA_ERR_NUMBER$                                   NUMBER
 ORA_ERR_MESG$                                     VARCHAR2(2000)
 ORA_ERR_ROWID$                                    ROWID
 ORA_ERR_OPTYP$                                    VARCHAR2(2)
 ORA_ERR_TAG$                                      VARCHAR2(2000)
 X                                                 VARCHAR2(4000)

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> create or replace procedure insert_rows( p_low in number, p_high in number )
  2  as
  3      l_tag varchar2(44);
  4  begin
  5      select error_log_seq.nextval
  6        into l_tag
  7        from dual;
  8
  9      insert into t (x)
 10      select p_low+level-1
 11        from dual
 12      connect by level <= (p_high-p_low+1)
 13      LOG ERRORS (l_tag)
 14      reject limit unlimited;
 15
 16      dbms_output.put_line( 'sql%rowcount = ' || sql%rowcount );
 17
 18      for x in (select count(*) cnt
 19                  from err$_t
 20                 where  ORA_ERR_TAG$ = l_tag )
 21      loop
 22          dbms_output.put_line( x.cnt || ' errors for tag = ' || l_tag );
 23      end loop;
 24  end;
 25  /

Procedure created.

ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> exec insert_rows( 1, 10 );
sql%rowcount = 10
0 errors for tag = 1

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> exec insert_rows( 0, 20 );
sql%rowcount = 20
1 errors for tag = 2

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> exec insert_rows( -100, 100 );
sql%rowcount = 100
101 errors for tag = 3

PL/SQL procedure successfully completed.

Got it!

Robert, April 05, 2010 - 4:26 pm UTC

Got it Tom. Thanks!

dbms_errlog

Snehasish Das, June 16, 2011 - 1:28 pm UTC

Hi Tom,

Does the use of DBMS_ERRLOG in sql queries hamper the performance of the query,as per my little knowledge There must be some inbuilt triggers which are logging these errors for DBMS_ERRLOG.

Please let me know if its a good practice to use this to all DMLs in our Data Ware House environment.

Regards,
Snehasish Das
Tom Kyte
June 17, 2011 - 1:42 pm UTC

It will have an impact when there are errors to record. And the impact will be positive - since the statement won't fail, won't roll back!


As I always say though - benchmark it.


Would it be good for all DML? No, it would only be good for DML that you expect an error to sometimes happen and it is OK to just log the error off to the side. If you are getting data that should be clean, should load - it would not make sense to use this, you'd want to STOP processing in that case.

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library