Compound triggers in oracle - oracle

I have a compound trigger and in the after statement I have an update to other table that has also a compound trigger, like in the code below:
create or replace
trigger TRIGGER
for insert or update on TABLE
COMPOUND trigger
after STATEMENT is
begin
update THEOTHERTABLE set VALUE = VALUE + 1 where COD = 1;
end after STATEMENT;
end;
The update is just a simple one to see if works. I want it to fire the trigger on THEOTHERTABLE, but it only fires if the trigger is not compound.
Is this a problem with Oracle compound triggers or just a feature which I am not understanding?

I have attempted to re-create your scenario, and it appears to work fine for me. So I think you should look again at your implementation. Look for the differences between what you have coded and what follows here, and perhaps that is where the answer lies.
Here are my triggers
SQL> create or replace
2 trigger t1_compound
3 for insert or update on t1
4 compound trigger
5
6 after statement is
7 begin
8 update t2 set t1_id = nvl(t1_id,0) + 1 where cod = 12;
9 end after statement;
10 end;
11 /
Trigger created.
SQL>
SQL> create or replace
2 trigger t2_compound
3 for insert or update on t2
4 compound trigger
5
6 after statement is
7 begin
8 update t3 set t2_id = nvl(t2_id,0) + 1 where cod = 12;
9 end after statement;
10 end;
11 /
Trigger created.
SQL>
... here is the test data ...
SQL> select id, cod from t1
2 /
ID COD
---------- ----------
1 12
SQL> select id, cod, t1_id from t2
2 /
ID COD T1_ID
---------- ---------- ----------
11 12
SQL> select id, cod, t2_id from t3
2 /
ID COD T2_ID
---------- ---------- ----------
111 12
SQL>
... and this is what happens when I issue an update on the first table ...
SQL> update t1 set dt = sysdate
2 where id = 1
3 /
1 row updated.
SQL> select id, cod, t1_id from t2
2 /
ID COD T1_ID
---------- ---------- ----------
11 12 1
SQL> select id, cod, t2_id from t3
2 /
ID COD T2_ID
---------- ---------- ----------
111 12 1
SQL>

It works if you do it after compiling the trigger but if you try it again, even with other data, it won't update THEOTHERTABLE

i don't think it should work...it would be better if you would create a procedure for THEOTHERTABLE and call that procedure from this trigger.

Related

PL/SQL Select and Update statement inside OPEN - FOR

So I have a pl/sql function and I want to select and update table inside open for statement
It looks like this:
table_a
id | status | document_id |
create or replace function a(p_document_id in number)
return sys_refcursor
is
result sys_refcursor;
begin
open result for
select id
from table_a t
where t.document_id = p_document_id;
update table_a t
set t.status = 1
where id in (select id
from table_a t
where t.document_id = p_document_id);
return result;
end;
/
But it is not working. Is there some method to do this? thanks in advance
This is a sample table:
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 0
Function you wrote compiles, but doesn't work because functions - normally - don't do DML:
SQL> select a(100) from dual;
select a(100) from dual
*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.A", line 11
SQL>
It, though, would work from PL/SQL, e.g.
SQL> declare
2 l_rc sys_refcursor;
3 begin
4 l_rc := a(100);
5 end;
6 /
PL/SQL procedure successfully completed.
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 1
SQL>
But, if you want to be able to call the function from PL/SQL, it has to be an autonomous transaction, which also means that you have to commit within:
SQL> rollback;
Rollback complete.
SQL> create or replace function a(p_document_id in number)
2 return sys_refcursor
3 is
4 result sys_refcursor;
5 pragma autonomous_transaction; --> this
6 begin
7 open result for
8 select id
9 from table_a t
10 where t.document_id = p_document_id;
11
12 update table_a t
13 set t.status = 1
14 where id in (select id
15 from table_a t
16 where t.document_id = p_document_id);
17
18 commit; --> this
19
20 return result;
21 end;
22 /
Function created.
Let's try it:
SQL> select a(100) from dual;
A(100)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ID
----------
1
SQL> select * from table_a;
ID DOCUMENT_ID STATUS
---------- ----------- ----------
1 100 1
SQL>
This might, or might not be OK. Autonomous transactions can be tricky and we usually use them for logging purposes, so that their commit doesn't affect the main transaction.

How to duplicate records with triggers?

CREATE OR REPLACE TRIGGER "DUPLICATE_FOO" AFTER INSERT ON "FOO" FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
insert into remote_foo values(:new); -- can I do this?
EXCEPTION
-- TODO log somewhere
END;
Is there an elegant way to create a trigger that basically duplicates a record from one table to another?
I would like to avoid having to specify the fields of the table since it will mean that the trigger would have to be updated in case there are changes in the schema (the remote scheme would be updated of course). I have like a dozen of tables more.
All the examples I have found always specify the fields in the insert (:new.fieldX).
The keyword here is NOT to specify column names, right? In my opinion, you should because that's the only way you can control it.
the remote scheme would be updated of course
is kind of dangerous. WHAT IF it doesn't happen? "Of course" works until it does not work.
Sample tables (both are empty):
SQL> create table foo (id number, name varchar2(20));
Table created.
SQL> create table remote_foo as select * From foo where 1 = 2;
Table created.
If you use a trigger which is an autonomous transaction, then it won't see :new pseudorecord (as this is an autonomous transaction; right?); to this trigger, select * from foo where id = :new.id; won't return anything and remote_foo remains empty:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo select * from foo where id = :new.id;
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
1 Littlefoot
SQL> select * from remote_foo; --> it remained empty
no rows selected
SQL>
Note that - if you specified columns - it would work (but that's not what you wanted):
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 for each row
4 declare
5 pragma autonomous_transaction;
6 begin
7 insert into remote_foo (id, name) values (:new.id, :new.name);
8 commit;
9 end;
10 /
Trigger created.
SQL> insert into foo (id, name) values (2, 'Bigfoot');
1 row created.
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
2 Bigfoot
SQL>
So, what to do? Switch to a statement-level trigger (instead of a row-level): it doesn't have to be autonomous, but has to have something that will prevent duplicates to be inserted - for example, a NOT EXISTS clause:
SQL> create or replace trigger trg_ai_foo
2 after insert on foo
3 begin
4 insert into remote_foo
5 select * from foo a
6 where not exists (select null from remote_foo b
7 where b.id = a.id);
8 end;
9 /
Trigger created.
SQL> insert into foo (id, name) values (1, 'Littlefoot');
1 row created.
SQL> insert into foo (id, name)
2 select 2, 'Bigfoot' from dual union all
3 select 3, 'anat0lius' from dual;
2 rows created.
Result:
SQL> select * from foo;
ID NAME
---------- --------------------
2 Bigfoot
3 anat0lius
1 Littlefoot
SQL> select * from remote_foo;
ID NAME
---------- --------------------
1 Littlefoot
3 anat0lius
2 Bigfoot
SQL>

How to store and retrieve data from temp table

I am new to PL-SQL. Please can someone help me on wrapping the below.
I need to:
Copy contents of table A and store in global temp table
Do an update on Table A & other tasks
Revert contents of Table A from global temp table
Delete temp table
ANy help is much appreciated.
Here you are:
Sample tables:
SQL> create table test as select * from dept;
Table created.
SQL> create global temporary table gtt_test as
2 select * from test where 1 = 2;
Table created.
SQL> select * from test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
1 Dept 1 NY
2 Dept 2 London
6 rows selected.
SQL> select * from gtt_test;
no rows selected
Anonymous PL/SQL block which does what you asked:
SQL> begin
2 -- copy contents ...
3 insert into gtt_test select * from test;
4
5 -- update A
6 update test set loc = 'blabla';
7 delete from test where deptno = 20;
8
9 -- revert
10 delete from test;
11 insert into test select * from gtt_test;
12
13 -- delete temp
14 delete from gtt_test;
15 end;
16 /
PL/SQL procedure successfully completed.
At the end, no changes whatsoever:
SQL> select * from test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
1 Dept 1 NY
2 Dept 2 London
6 rows selected.
SQL> select * from gtt_test;
no rows selected
SQL>
[EDIT]
Query you posted as a comment isn't correct; dynamic SQL must be enclosed into single quotes, it must not be terminated by a semi-colon while all other statements must. Fixed, it should be
BEGIN
EXECUTE IMMEDIATE 'create table camel_route_temp as '
|| ' select * from acquire.CAMEL_ROUTE_PROCESS';
DBMS_OUTPUT.put_line ('Temp table created');
--update A
UPDATE acquire.camel_route_process
SET acquire = 'DISABLED',
assure = 'DISABLED',
validation = 'DISABLED',
report = 'DISABLED',
notification = 'DISABLED';
COMMIT;
DBMS_OUTPUT.put_line ('Aquire components disabled');
END;
/

Change detection capture in Oracle DB tables

I wanted to if it is possible to detect changes in Oracle DB table for each column and its value and capture the change is a separate temporary table ?
Yes; people usually do that using triggers.
Here's a simple example:
SQL> create table dept_log
2 (deptno number,
3 dname varchar2(20),
4 loc varchar2(20),
5 when date
6 );
Table created.
SQL>
SQL> create or replace trigger trg_bu_dept
2 before update on dept
3 for each row
4 begin
5 if :new.dname <> :old.dname or
6 :new.loc <> :old.loc
7 then
8 insert into dept_log (deptno, dname, loc, when)
9 values (:new.deptno, :old.dname, :old.loc, sysdate);
10 end if;
11 end;
12 /
Trigger created.
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> update dept set loc = 'LONDON' where deptno = 40;
1 row updated.
SQL> select * From dept_log;
DEPTNO DNAME LOC WHEN
---------- -------------------- -------------------- -------------------
40 OPERATIONS BOSTON 11.04.2018 22:00:23
SQL>

Simulating constraint ON UPDATE option by TRIGGER

I'm trying to simulate ON UPDATE option in my foreign keys by making trigger that will update child records after update.
Trigger looks like this:
CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE ID = :OLD.ID;
END;
And I'm making the child table constraint deferrable like this:
set constraint
SOMECHILDTABLE_FK_SOMETABLE deferred;
But it still gives me ORA-02292 (child record found) on commit. How can I avoid this?
Kudos to #BrianCamire for spotting the bug:
Let's start by creating your trigger...
SQL> CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE ID = :OLD.ID;
END; 2 3 4 5 6
7 /
Trigger created.
SQL> alter table somechildtable add constraint some_fk foreign key (sometable_id)
2 references sometable (id) deferrable initially deferred;
Table altered.
SQL>
Here is the data ...
SQL> select * from somechildtable;
ID SOMETABLE_ID
---------- ------------
11 1
12 2
13 6
14 4
15 5
SQL>
So, let's mess with the parent data....
SQL> update sometable set id = 3 where id = 6
2 /
1 row updated.
SQL> commit;
commit
*
ERROR at line 1:
ORA-02091: transaction rolled back
ORA-02292: integrity constraint (APC.SOME_FK) violated - child record found
SQL>
Aha! So, as Brian says we need to change the trigger code:
SQL> CREATE OR REPLACE TRIGGER SOMETABLE_AU_ID_TRG
AFTER UPDATE OF ID ON SOMETABLE
FOR EACH ROW
BEGIN
UPDATE SOMECHILDTABLE SET SOMETABLE_ID = :NEW.ID WHERE SOMETABLE_ID = :OLD.ID;
END;
/
2 3 4 5 6 7
Trigger created.
SQL> SQL> update sometable set id = 3 where id = 6;
1 row updated.
SQL> commit;
Commit complete.
SQL> select * from somechildtable
2 /
ID SOMETABLE_ID
---------- ------------
11 1
12 2
13 3
14 4
15 5
SQL>

Resources