Home » SQL & PL/SQL » SQL & PL/SQL » DB_Link inside Trigger Breaking
DB_Link inside Trigger Breaking [message #8659] Wed, 17 September 2003 13:04 Go to next message
Michele
Messages: 77
Registered: December 2000
Member
Hi everyone,

I have a problem that I can't understand. We have a stored procedure that has been running fine. This stored procedure was called from a Visual Basic application on a Windows 2000 machine.

The stored procedure inserts and deletes records from table A. There is a trigger on this table that is define below. Bascially, when a record is deleted or inserted on Table A, a trigger is fired to delete or insert it from Table B on a remote database using a db_link.

CREATE OR REPLACE TRIGGER TEST_TABLEA_ID AFTER INSERT
OR DELETE ON "PROD"."CNTRCTR_EXT" FOR EACH ROW
BEGIN

IF DELETING THEN

DELETE FROM TEMS.TABLE_B@TEST_LK.WORLD
WHERE pers_id = :OLD.pers_id;

--ELSIF INSERTING THEN
ELSE
INSERT INTO TEMS.TABLE_B@TEST_LK.WORLD
VALUES ( :NEW.pers_id ,
:NEW.last_name,
:NEW.first_name ,
:NEW.mi,
:NEW.prefix ,
:NEW.fax1
);

END IF;

END;

The problem is we are not using the Visual Basic application anymore to call this stored procedure. This stored procedure will be called via another application. Now I have been running tests from my windows 2000 machine using SQL*Plus calling this stored procedure. The first time runs successfully. The second time I make a call to the proc, I get a primary key violation. It turns out that when the record is deleted from Table A locally, the trigger is not deleting the record from the remote database. So, when the trigger tries to insert the record into the remote database, it throws this exception because it exists in the table.

What I can't understand is why this is happening. We are using Oracle 9i locally and the remote database is on Oracle 8i.

But why is this happening. Why would the trigger have no problems when the proc was call from a VB app vs SQL*Plus.?

Is something causing the db_link to break? I did try to disable the trigger and put the trigger code directly into the stored procedure and that seems to have no problem.

Any insights will be greatly appreciated.

Thanks
Michele
Re: DB_Link inside Trigger Breaking [message #8661 is a reply to message #8659] Wed, 17 September 2003 14:46 Go to previous messageGo to next message
Todd Barry
Messages: 4819
Registered: August 2001
Senior Member
Michele, your layout looks fine to me. Is the goal to keep these two tables exactly the same? Or is one table a subset of the other?

If they are supposed to be identical, a materialized view (snapshot) would likely be a better choice here than a trigger.

1) Make sure the trigger is enabled (obvious, I know, but it happens).

2) I would assume your local table has a PK that would not allow the same pers_id to be added twice. If so, then somehow the delete is not happening on the remote table. I would suggest some debugging code (dbms_output or insert into a log table) in the trigger to track the executions.

3) Is there any chance the PK constraint on the local table is deferred and it is not on the remote table?
Re: DB_Link inside Trigger Breaking [message #8663 is a reply to message #8661] Wed, 17 September 2003 15:25 Go to previous messageGo to next message
Michele
Messages: 77
Registered: December 2000
Member
HI Todd,

1) Yes, the goal is to keep these two tables in sync.
2) I have added the error handling and I seem to get back the primary key violation error and not the error that cause the violation which is not deleting the record from the remote database once it is deleted from the local database.

I added the below code after the delete and insert in the trigger:

dbms_output.put_line('Deleted:'||sql%rowcount);
dbms_output.put_line('Inserted:'||sql%rowcount);

Exception
when other then
dbms_output.put_line(sqlerrm);

3)As far as the primary key being deferred, I am not familiar with that so I will have to check.

The weird thing is that when I execute the stored procedure once, it runs fine. If I just keep executing the stored proc after that it just keeps failing. So then I noticed that if I re-compile the trigger(eventhough it is valid) and run the proc again it runs fine. Then I try again and it fails. Only when I re-compile the trigger does it work fine.

Driving me nuts...

I am not familiar with materialized views and how to use them, I will have to do some reading on them.

Michele :-)
Re: DB_Link inside Trigger Breaking [message #8664 is a reply to message #8663] Wed, 17 September 2003 16:14 Go to previous messageGo to next message
Todd Barry
Messages: 4819
Registered: August 2001
Senior Member
Would it be possible to post your procedure code (or the relevant section)?

Recompiling the trigger is definitely not what is causing it to "run fine". It may seem that way, but it really isn't...

What does your debugging code show when you first insert and then delete from the local table? Does it show the rows being inserted and deleted from the remote table?
Re: DB_Link inside Trigger Breaking [message #8682 is a reply to message #8664] Thu, 18 September 2003 07:51 Go to previous messageGo to next message
Michele
Messages: 77
Registered: December 2000
Member
Hi Todd,

My debugging code show 1 record being deleted and 1 record being inserted on the remote table. And the second time I execute it it shows 0 deleted and 0 inserted and the primary key violation error.
Could there be a problem going from 9i(TABLEA) to 8i(TABLEB).

My procedure code is below. I cut alot of the fields out to save space. Also, I noticed that my trigger code I sent yesterday showed a trigger on Prodx.Cntrctr_ext. It is really on TableA for the purpose of this question. Forgot to change it.

CREATE OR REPLACE TRIGGER TEST_TABLEA_ID AFTER INSERT
OR DELETE ON "PROD"."TABLEA" FOR EACH ROW
BEGIN

IF DELETING THEN

DELETE FROM TEMS.TABLE_B@TEST_LK.WORLD
WHERE pers_id = :OLD.pers_id;

--ELSIF INSERTING THEN
ELSE
INSERT INTO TEMS.TABLE_B@TEST_LK.WORLD
VALUES ( :NEW.pers_id ,
:NEW.last_name,
:NEW.first_name ,
:NEW.mi,
:NEW.prefix ,
:NEW.fax1
);

END IF;
END;

There are actually 2 stored procedures that get fired. One proc calls another proc. The second proc is the one that inserts into TABLEA.

CREATE OR REPLACE PROCEDURE UPDATEPROD
(PERSID IN NUMBER)
is

ACTIVE_IND char(1);

BEGIN

SELECT ACTIVE_IND INTO ACTIVE_IND FROM WRK.CNTRCTR W WHERE W.PERS_ID = PERSID;
IF ACTIVE_IND = 'A' THEN
UPDATE WRK.CNTRCTR W SET W.MOVE_DATE = SYSDATE WHERE W.PERS_ID = PERSID;
DELETE FROM PROD.CNTRCTR P WHERE P.PERS_ID = PERSID;
INSERT INTO PROD.CNTRCTR
SELECT * FROM WRK.CNTRCTR W WHERE W.PERS_ID = PERSID;
INSERT INTO PROD.COMM_NUMBER
SELECT * FROM WRK.COMM_NUMBER W WHERE W.PERS_ID = PERSID;
ELSE
DELETE FROM PROD.CNTRCTR P WHERE P.PERS_ID = PERSID;
END IF;
COMMIT;

PROD.RM_CNTRCTR_EXTRACT15(PERSID);

EXCEPTION
WHEN OTHERS THEN
-- WRKX.ERROR_HANDLER.PROCESS_ERROR(sqlerrm,sqlcode,PERSID);
dbms_output.put_line(sqlerrm);
END;

CREATE OR REPLACE PROCEDURE RM_CNTRCTR_EXTRACT15
(PERSID IN NUMBER)

IS
V_toutput prod.TABLEA%ROWTYPE;

BEGIN

DELETE FROM PROD.TABLEA P WHERE P.PERS_ID = PERSID;
COMMIT;

FOR myCNTRCTR IN (SELECT a.pers_id, a.rec_id, a.last_name, a.first_name, a.mi, a.prefix, b.vocab FROM CNTRCTR a, DICTIONARY b
WHERE b.trans_code = a.rep_status
AND b.tab_name = 'CNTRCTR'
AND b.col_name = 'REP_STATUS'AND a.pers_id = PERSID)

LOOP
-- columns from CNTRCTR
V_toutput.pers_id:=myCNTRCTR.pers_id;
V_toutput.last_name:=myCNTRCTR.last_name;
V_toutput.first_name:=myCNTRCTR.first_name;
V_toutput.mi:=myCNTRCTR.mi;
V_toutput.prefix:=myCNTRCTR.prefix;

-- column from comm_number
V_toutput.fax1:=NULL;

FOR myF1COMM_NUMBER IN ( SELECT * FROM COMM_NUMBER
WHERE pers_id = myCNTRCTR.pers_id AND comm_type = 'FAX' AND sort_no = 1)

LOOP
V_toutput.fax1:=myF1COMM_NUMBER.comm_no|| ' ' || myF1COMM_NUMBER.ext;
END LOOP;

INSERT INTO PROD.TABLEA VALUES
(
V_toutput.pers_id,
V_toutput.last_name,
V_toutput.first_name,
V_toutput.mi,
V_toutput.prefix,
V_toutput.fax1
);

COMMIT;

END LOOP;

EXCEPTION
--WHEN OTHERS THEN
-- WRKX.ERROR_HANDLER.PROCESS_ERROR(sqlerrm,sqlcode,PERSID);
when others then
dbms_output.put_line(sqlerrm);
END;

Thanks for looking it over Todd. Much appreciated

Michele :-)
Re: DB_Link inside Trigger Breaking [message #8686 is a reply to message #8682] Thu, 18 September 2003 11:54 Go to previous messageGo to next message
Todd Barry
Messages: 4819
Registered: August 2001
Senior Member
Michelle, as a starting point, here are some mods to your code that I would suggest:

1) Prefix parameters (p_) and variables (v_) so they can be easily identified and distinguished.

2) Type those parameters and variables to actual columns if possible using %TYPE.

3) Only use table aliases on multi-table joins.

4) Only commit once at the conclusion of the transaction. Either everything should happen or none of it. I have left only one commit and that is in the top-level proc.

5) No need to assign all the cursor variable values to the rowtype variable before the insert in the second proc. The fax value is the only dynamic value in that context.

I'm a bit confused by the cursor loop in that second procedure. Will that result set (based on the query involving the cntrctr and dictionary tables) return more than one row for a given pers_id? If so, wouldn't that cause the PK violation on tableA? If not, this should just be an insert into/select from.

Also, since you are not concatenating the fax value, it appears that the fax query is also just a single row result that could be included into the insert into/select from statement mentioned above.

We'll get to the bottom of this...

-Todd

create or replace procedure updateprod(
  p_pers_id  in  wrk.cntrctr.pers_id%type)
is
  v_active_ind  wrk.cntrctr.active_ind%type;
begin
  select active_ind
    into v_active_ind
    from wrk.cntrctr
   where pers_id = p_pers_id;
  
  if v_active_ind = 'A' then
    update wrk.cntrctr
       set move_date = sysdate
     where pers_id = p_pers_id;
 
    delete from prod.cntrctr
     where p.pers_id = p_pers_id;
 
    insert into prod.cntrctr
      select *
        from wrk.cntrctr
       where pers_id = p_pers_id;

    insert into prod.comm_number
      select *
        from wrk.comm_number
       where pers_id = p_pers_id;
  else
    delete from prod.cntrctr
     where pers_id = p_pers_id;
  end if;
 
  prod.rm_cntrctr_extract15(p_pers_id);
   
  commit;
 
exception
  when others then
    -- WRKX.ERROR_HANDLER.PROCESS_ERROR(sqlerrm,sqlcode,PERSID);
    dbms_output.put_line(sqlerrm);
end;
 
create or replace procedure rm_cntrctr_extract15(
  p_pers_id  in  cntrctr.pers_id%type)
is
  v_fax1  prod.tablea.fax1%type;
begin
  delete 
    from prod.tablea
   where pers_id = p_pers_id;
 
  for mycntrctr in (select a.pers_id,
                           a.last_name,
                           a.first_name,
                           a.mi,
                           a.prefix
                      from cntrctr a,
                           dictionary b
                     where b.trans_code = a.rep_status
                       and b.tab_name = 'CNTRCTR'
                       and b.col_name = 'REP_STATUS'
                       and a.pers_id = p_pers_id) loop
 
    v_fax1 := null;
 
    for myf1comm_number in (select *
                              from comm_number
                             where pers_id = mycntrctr.pers_id
                               and comm_type = 'FAX'
                               and sort_no = 1) loop
      v_fax1 := myf1comm_number.comm_no || ' ' || myf1comm_number.ext;
    end loop;
 
    insert into prod.tablea
         values (mycntrctr.pers_id,
                 mycntrctr.last_name,
                 mycntrctr.first_name,
                 mycntrctr.mi,
                 mycntrctr.prefix,
                 v_fax1);
 
  end loop;
exception
  --WHEN OTHERS THEN
   -- WRKX.ERROR_HANDLER.PROCESS_ERROR(sqlerrm,sqlcode,PERSID);
  when others then
    dbms_output.put_line(sqlerrm);
end;
Re: DB_Link inside Trigger Breaking [message #8710 is a reply to message #8686] Fri, 19 September 2003 10:39 Go to previous messageGo to next message
Michele
Messages: 77
Registered: December 2000
Member
Hi Todd,

Thanks for your suggestions. I will use those standards in the future. I wrote the updateprod procedure. The rm_cntrctr_extract15 was written by someone else.
I modified the proc to only one commit in the master proc like you said.
There are about 8 more detail tables and for loops that are used in the rm_cntrctr_extract15 so I think that is why the developer assigned the cursor variables to the row variable.

The query between the cntrctr and dictionary table definitely returns only 1 row. I cut alot of fields out to save space.

You mentioned using the insert into select from statement. I am not sure I understand what is suppose to replace...

I did some testing this morning and still the same problem. We have a backup, which is disabling the trigger and putting the trigger in the stored procedure and that seems to have no problems. But this is just urking me :-(
Re: DB_Link inside Trigger Breaking [message #8711 is a reply to message #8710] Fri, 19 September 2003 11:36 Go to previous messageGo to next message
Todd Barry
Messages: 4819
Registered: August 2001
Senior Member
The insert into/select approach would look like:

create or replace procedure rm_cntrctr_extract15(
  p_pers_id  in  cntrctr.pers_id%type)
is
begin
  delete 
    from prod.tablea
   where pers_id = p_pers_id;
 
  insert into prod.tablea
    select a.pers_id,
           a.last_name,
           a.first_name,
           a.mi,
           a.prefix,
           cn.comm_no || ' ' || cn.ext
      from cntrctr a,
           dictionary b,
           comm_number cn
     where a.pers_id = p_pers_id
       and b.trans_code = a.rep_status
       and b.tab_name = 'CNTRCTR'
       and b.col_name = 'REP_STATUS'
       and cn.pers_id = a.pers_id
       and cn.comm_type = 'FAX'
       and cn.sort_no = 1;
exception
  when others then
    dbms_output.put_line(sqlerrm)
    raise;
end;


The join to the comm_number table would need to be an outer join (+) if there is the possibility that there may not be a matching entry there for every pers_id.

Why is a backup disabling the trigger? I don't understand the correlation there.
Re: DB_Link inside Trigger Breaking [message #8713 is a reply to message #8711] Fri, 19 September 2003 12:42 Go to previous message
Michele
Messages: 77
Registered: December 2000
Member
Hi Todd,

Sorry I misspoke. I meant to say that we have a backup plan which has the trigger code in the rm_cntrctr_extract15 procedure and we disable the original trigger from prod.tablea so it isn't fired. Executing the modified proc doesn't seem to give us any errors like the trigger has done in the past. Ex:

rm_cntrctr_extract15 proc:

delete from prod.tablea p where p.pers_id = persid;
delete from tems.table_b@test_lk.world where p.pers_id = persid;
commit
etc...

Michele
Previous Topic: UTL_FILE and the root directory
Next Topic: Average date/time
Goto Forum:
  


Current Time: Wed Apr 24 01:45:41 CDT 2024