Вопрос

I am trying to maintain data integrity and logging errors to an error table. I've got 3 tables with unique constraints and 1 error table:

create table tbl_one ( 
pass_no number, 
constraint tbl_one_u01 unique (pass_no) );
insert into tbl_one values(10);
insert into tbl_one values(20);

create table tbl_two ( 
cus_no number, 
cus_name varchar2(50), 
pass_no number,
constraint tbl_two_u01 unique (cus_no) );
insert into tbl_two values( 101, 'NameX',10);
insert into tbl_two values( 102, 'NameY',10);
insert into tbl_two values( 103, 'NameZ',20);

create table tbl_target (
cus_no number,
pass_no number,
constraint tbl_target_u01 unique (cus_no),
constraint tbl_target_u02 unique (pass_no));

exec dbms_errlog.create_error_log('tbl_target','tbl_target_err');

I am trying to log all ORA-00001 errors to the error table tbl_target_err like this:

begin
    insert into tbl_target
    select a.pass_no, b.cus_no
      from tbl_one a 
     inner join tbl_two b on b.pass_no = a.pass_no
       log errors into tbl_target_err reject limit 10;
end;

The result is:

select * from tbl_target;
-------------------
CUS_NO    PASS_NO
101    10
103    20

and the error table:

CUS_NO    PASS_NO
102    10

I need all the violated errors to go into the error table. If the value of pass_no 10 is violated then all 10 values should inserted into the error table; not, one into target and one to error table. I don't want to use exists statements because I won't able to log all violated values.

How could I go about doing this?

Это было полезно?

Решение

You can't use the error logging mechanism for this as it isn't designed to support it. It errors at the point it tries to create the duplicate in the table - the first value it tries to insert for pass_no 10 is valid on its own - so it would have to distinguish between data that already existed and multiple values coming from the insert, to start with. So you'd need to roll your own.

One option is to create your own table to hold the duplicates, and use an insert all to decide which values belong in each table:

create table tbl_target_dup (
cus_no number,
pass_no number
);

insert all
when cus_count = 1 and pass_count = 1 then
  into tbl_target values (cus_no, pass_no)
else
  into tbl_target_dup values (cus_no, pass_no)
select a.pass_no, b.cus_no,
  count(*) over (partition by a.pass_no) as pass_count,
  count(*) over (partition by b.cus_no) as cus_count
from tbl_one a 
join tbl_two b on b.pass_no = a.pass_no;

This allows you to have more columns than those affected by the PK/UK, and insert them only into the real table if you prefer, or a subset into the 'error' table. With just those two columns in each table you get:

select * from tbl_target;

    CUS_NO    PASS_NO
---------- ----------
       103         20 

select * from tbl_target_dup;

    CUS_NO    PASS_NO
---------- ----------
       101         10 
       102         10 

SQL Fiddle demo.

You could do the same thing with two inserts based on the same select, one with a subquery checking that both counts are 1, other checking that at least one is not, but this might perform better.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top