Database Trigger
Shajan Joseph, May 01, 2002 - 3:17 am UTC
question dealing with ORA-02292 child record found
A reader, March 13, 2003 - 4:46 pm UTC
Hi
I have a question regarding TRIGGERS
I have a trigger when a child table record is deleted then the trigger fires and tries to delete the parent record, for example when I delete employee FORD whose DEPTNO is 10 the trigger will try to delete deptno 10 in dept table. Obviously I get ORA-02292 however I tried to deal with this as an exception in the trigger but it does not work!
my code is
create or replace trigger emp_trg
after delete on emp
for each row
declare
child exception;
pragma exception_init(child, -2292 );
begin
insert into x values(:old.deptno);
delete dept where deptno = 10;
exception
when child
then
raise_application_error(-20001, 'ERROR1');
end;
/
insert into x is a debug part, checking if triggers gets fired
March 14, 2003 - 5:50 pm UTC
can you give me a REAL WORLD example whereby this would actually be useful?
Exception handling
binu sakharia, June 30, 2003 - 12:42 am UTC
greetings,
This question is regarding Java and Oracle and I wish u could help me!!
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@"+ip_address+":"+port_no+":"+sid, user, paswd);
String Sql_stmt=<<insert statement>>
stmt=conn.createStatement();
try
{
res_set=stmt.executeQuery(Sql_stmt);
}
catch(Exception e)
{
chk_flag=3;
write_log_file(Sql_stmt,e,"o");
}
this is my java coding and I have oracle trigger. This trigger takes the voucher_no,debit_amount and credit_amount of the new row inserted into table "voucher" and checks for the availablity of the voucher_no in table "GLBAL" and correspondingly update the debit and credit amount of the voucher_no in the GLBAL.When the voucher_no is not found in the "GLBAL" the trigger automatically fails thus insertion into the voucher also fails.The exception raised by the oracle trigger cannot be captured by the try..catch block in the java program.The program is executed as if there is noo error occured.but in reality the rows has not been inserted into oracle. so is there any work around for this.
Thanxs in advance
June 30, 2003 - 7:01 am UTC
#1 -- you must, as in have to, use bind variables -- especially for an update/insert/delete. It is imperative.
#2 -- i cannot reproduce your findings. No versions, no platform, incomplete test case. Sorry. My tests work fine.
ops$tkyte@ORA920> create table t ( x int );
Table created.
ops$tkyte@ORA920> create or replace trigger t_trigger
2 before insert on t for each row
3 begin
4 raise program_error;
5 end;
6 /
Trigger created.
public static void Bind( int input )
throws SQLException
{
PreparedStatement stmt = conn.prepareStatement("insert into t values ( ? )");
try
{
stmt.setInt(1,input);
stmt.executeQuery();
stmt.close();
System.out.println( "Success" );
}
catch (Exception e)
{
System.out.println( e.getMessage() );
}
}
public static void main(String[] args) throws Exception
{
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
conn = DriverManager.getConnection
("jdbc:oracle:thin:@aria-dev:1541:ora920"
//("jdbc:oracle:oci8:@ora816dev.us.oracle.com"
,"ops$tkyte","megan95");
conn.setAutoCommit(false);
Bind(5000);
conn.close();
}
$ java test
ORA-06501: PL/SQL: program error
ORA-06512: at "OPS$TKYTE.T_TRIGGER", line 2
ORA-04088: error during execution of trigger 'OPS$TKYTE.T_TRIGGER'
error raised, exception caught.
Ignore rather than catch an ORA-02292
Rick Davis, September 27, 2006 - 4:42 pm UTC
Tom, again, thanks for your outstanding contributions to the Oracle community! Please consider the flip side of this thread. The situation is we wish to delete a supertype (parent) entry when a subtype (child) entry is deleted if we can, which implies the parent has no other child. If it has other children, trap the ORA-02292 so the child delete can complete/commit. Here's what we tried:
CREATE OR REPLACE TRIGGER TD_SND_NT_AA_CT_SUB_CP
AFTER DELETE ON SND_NT_AA_CT_SUB_CP
FOR EACH ROW
DECLARE
child_found exception;
pragma exception_init ( child_found , -2292);
begin
delete from SND_NON_TOOL_AA_SUB_CP
where
SND_NON_TOOL_AA_SUB_CP.PUB_SHRT_TTL_TX = :old.PUB_SHRT_TTL_TX and
SND_NON_TOOL_AA_SUB_CP.PUB_ISS_C_DT = :old.PUB_ISS_C_DT and
SND_NON_TOOL_AA_SUB_CP.PHS_NM = :OLD.S_PHS_NM and
SND_NON_TOOL_AA_SUB_CP.PROC_ACTV_ID = :old.S_PROC_ACTV_ID and
SND_NON_TOOL_AA_SUB_CP.CMNQ_PART_ID = :old.S_CMNQ_PART_ID and
SND_NON_TOOL_AA_SUB_CP.ASG_ACT_VRS_ID = :old.S_ASG_ACT_VRS_ID and
SND_NON_TOOL_AA_SUB_CP.SUB_CMNQ_PART_ID = :old.SUB_CMNQ_PART_ID;
EXCEPTION
when child_found then null;
end;
/
The trigger compiles but doesn't work -- it still throws the ORA-02292, ... child record found, and of course rolls off the delete of the child. Any ideas?
September 27, 2006 - 5:31 pm UTC
example, from start to finish - with FEW columns - just reproduce issue.
ops$tkyte%ORA9IR2> create table p ( x int primary key );
Table created.
ops$tkyte%ORA9IR2> create table c ( x references p );
Table created.
ops$tkyte%ORA9IR2> create or replace trigger t
2 after delete on c for each row
3 begin
4 dbms_output.put_line( 'delete ' || :old.x );
5 delete from p where x = :old.x;
6 end;
7 /
Trigger created.
ops$tkyte%ORA9IR2> insert into p values ( 1 );
1 row created.
ops$tkyte%ORA9IR2> insert into c values ( 1 );
1 row created.
ops$tkyte%ORA9IR2> commit;
Commit complete.
ops$tkyte%ORA9IR2> select * from p;
X
----------
1
ops$tkyte%ORA9IR2> delete from c where x = 1;
delete 1
1 row deleted.
ops$tkyte%ORA9IR2> select * from p;
no rows selected
Performance vs Timeliness
Jim, September 28, 2006 - 7:01 am UTC
If your inserts/deletes on the other tables could wait
I would also consider recoding so that the trigger just inserts into a work table.
Creating a job that runs periodically that reads the table, does the appropriate inserts/deletes and then removes the row from the work table.
Or a combination of both. The trigger does the quick simple things and postpones the longer processes to the work table to be done later.
Igonore ORA-02292
Rick Davis, September 28, 2006 - 8:25 am UTC
Tom, here's an example of our problem as streamlined as I can get it. Your keen insights appreciated.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.6.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.6.0 - Production
SQL> CREATE TABLE P ( X INT PRIMARY KEY );
Table created.
SQL>
SQL> CREATE TABLE C ( X INT, Y INT );
Table created.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT PKC PRIMARY KEY (X, Y));
Table altered.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT R_1 FOREIGN KEY (X) REFERENCES P (X));
Table altered.
SQL>
SQL> CREATE OR REPLACE TRIGGER T
2 AFTER DELETE ON C
3 FOR EACH ROW
4 DECLARE
5 CHILD_EXISTS EXCEPTION;
6 PRAGMA EXCEPTION_INIT (CHILD_EXISTS, -2292);
7 BEGIN
8 DELETE FROM P WHERE X = :OLD.X;
9 EXCEPTION
10 WHEN CHILD_EXISTS THEN
11 NULL;
12 END;
13 /
Trigger created.
SQL>
SQL> INSERT INTO P VALUES (1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,2);
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> DELETE FROM C WHERE X = 1 AND Y = 1;
DELETE FROM C WHERE X = 1 AND Y = 1
*
ERROR at line 1:
ORA-02292: integrity constraint (CRIS1.R_1) violated - child record found
September 29, 2006 - 6:05 am UTC
interesting, not sure why it does that, but it made the exception uncatchable. i'll bug that.
but in the meanwhile:
ops$tkyte%ORA9IR2> CREATE TABLE P ( X INT PRIMARY KEY );
Table created.
ops$tkyte%ORA9IR2> CREATE TABLE C ( X INT, Y INT );
Table created.
ops$tkyte%ORA9IR2> ALTER TABLE C ADD (CONSTRAINT PKC PRIMARY KEY (X, Y));
Table altered.
ops$tkyte%ORA9IR2> ALTER TABLE C ADD (CONSTRAINT R_1 FOREIGN KEY (X) REFERENCES P (X));
Table altered.
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> create or replace package my_pkg
2 as
3 type array is table of varchar2(1) index by binary_integer;
4 g_data array;
5 end;
6 /
Package created.
ops$tkyte%ORA9IR2> create or replace trigger t_bd
2 before delete on c
3 begin
4 my_pkg.g_data.delete;
5 end;
6 /
Trigger created.
ops$tkyte%ORA9IR2> create or replace trigger t_adfer
2 after delete on c for each row
3 begin
4 my_pkg.g_data(:old.x) := 'x';
5 end;
6 /
Trigger created.
ops$tkyte%ORA9IR2> create or replace trigger t_ad
2 after delete on c
3 declare
4 l_idx binary_integer;
5 CHILD_EXISTS EXCEPTION;
6 PRAGMA EXCEPTION_INIT (CHILD_EXISTS, -2292);
7 begin
8 l_idx := my_pkg.g_data.first;
9 while (l_idx is not null)
10 loop
11 begin
12 delete from p where x = l_idx;
13 exception
14 when child_exists
15 then
16 dbms_output.put_line( 'skipped ' || l_idx );
17 end;
18 l_idx := my_pkg.g_data.next(l_idx);
19 end loop;
20 end;
21 /
Trigger created.
ops$tkyte%ORA9IR2> pause
ops$tkyte%ORA9IR2>
ops$tkyte%ORA9IR2> INSERT INTO P VALUES (1);
1 row created.
ops$tkyte%ORA9IR2> INSERT INTO C VALUES (1,1);
1 row created.
ops$tkyte%ORA9IR2> INSERT INTO C VALUES (1,2);
1 row created.
ops$tkyte%ORA9IR2> COMMIT;
Commit complete.
ops$tkyte%ORA9IR2> DELETE FROM C WHERE X = 1 AND Y = 1;
skipped 1
1 row deleted.
ops$tkyte%ORA9IR2> select * from p;
X
----------
1
ops$tkyte%ORA9IR2> DELETE FROM C WHERE X = 1 AND Y = 2;
1 row deleted.
ops$tkyte%ORA9IR2> select * from p;
no rows selected
Ignore ORA-02292
Rick Davis, October 02, 2006 - 8:18 am UTC
Tom, how do I get on, and is there an "interested parties" list for the bug you filed? We agree with you; we feel the best solution is the following Trigger:
SQL> CREATE OR REPLACE TRIGGER T
2 AFTER DELETE ON C
3 FOR EACH ROW
4 DECLARE
5 CHILD_EXISTS EXCEPTION;
6 PRAGMA EXCEPTION_INIT (CHILD_EXISTS, -2292);
7 BEGIN
8 DELETE FROM P WHERE X = :OLD.X;
9 EXCEPTION
10 WHEN CHILD_EXISTS THEN
11 NULL;
12 END;
13 /
I'll be testing your solution soon, but for now here's another solution for those that might be interested. Problem with this solution is VIEW maintenance should C_REAL change over time.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.6.0 - Production
With the Partitioning, OLAP and Oracle Data Mining options
JServer Release 9.2.0.6.0 - Production
SQL> DROP TABLE C_REAL;
Table dropped.
SQL>
SQL> DROP VIEW C;
View dropped.
SQL>
SQL> DROP TABLE P;
Table dropped.
SQL>
SQL>
SQL> CREATE TABLE P ( X INT PRIMARY KEY );
Table created.
SQL>
SQL> CREATE TABLE C ( X INT, Y INT );
Table created.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT PKC PRIMARY KEY (X, Y));
Table altered.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT R_1 FOREIGN KEY (X) REFERENCES P (X));
Table altered.
SQL>
SQL> ALTER TABLE C RENAME TO C_REAL;
Table altered.
SQL>
SQL> CREATE VIEW C AS SELECT * FROM C_REAL;
View created.
SQL>
SQL> CREATE OR REPLACE TRIGGER CHILD_PARENT_DELETE
2 INSTEAD OF DELETE ON C
3 FOR EACH ROW
4 DECLARE
5 CHILD_EXISTS EXCEPTION;
6 PRAGMA EXCEPTION_INIT (CHILD_EXISTS, -2292);
7 BEGIN
8 DELETE FROM C_REAL
9 WHERE X = :OLD.X AND Y = :OLD.Y;
10
11 BEGIN
12 DELETE FROM P
13 WHERE X = :OLD.X;
14
15 EXCEPTION
16 WHEN CHILD_EXISTS THEN
17 NULL;
18 END;
19 END;
20 /
Trigger created.
SQL>
SQL> INSERT INTO P VALUES (1);
1 row created.
SQL>
SQL> INSERT INTO P VALUES (2);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,2);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (2,1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (2,2);
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT * FROM P;
X
----------
1
2
SQL>
SQL> SELECT * FROM C;
X Y
---------- ----------
1 1
1 2
2 1
2 2
SQL>
SQL> DELETE FROM C WHERE X = 1 AND Y = 1;
1 row deleted.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> SELECT * FROM P;
X
----------
1
2
SQL>
SQL> SELECT * FROM C;
X Y
---------- ----------
1 2
2 1
2 2
SQL>
SQL> DELETE FROM C WHERE X = 1 AND Y = 2;
1 row deleted.
SQL>
SQL> SELECT * FROM P;
X
----------
2
SQL>
SQL> SELECT * FROM C;
X Y
---------- ----------
2 1
2 2
October 02, 2006 - 8:24 am UTC
haven't filed the bug yet (don't see it as high priority, it'll be a p3 at best)
and just to be clear: I hate triggers, I really hate this class of trigger. "Automagic side effects happen as if by slight of hand". I really don't like this idea at all.
and that goes perhaps double for the instead of trigger on a view.
Oh yeah, "please expect deadlocks as a fact of life using this"
Ignore ORA-02292
Rick Davis, October 02, 2006 - 8:56 am UTC
Tom, your solution tested successfully. Thanks again for your outstanding support!!!
SQL> DROP TABLE C;
Table dropped.
SQL>
SQL> DROP TABLE P;
Table dropped.
SQL>
SQL> CREATE TABLE P ( X INT PRIMARY KEY );
Table created.
SQL>
SQL> CREATE TABLE C ( X INT, Y INT );
Table created.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT XPKC PRIMARY KEY (X, Y));
Table altered.
SQL>
SQL> ALTER TABLE C ADD (CONSTRAINT R_1 FOREIGN KEY (X) REFERENCES P (X));
Table altered.
SQL>
SQL> create or replace package my_pkg
2 as
3 type array is table of varchar2(1) index by binary_integer;
4 g_data array;
5 end;
6 /
Package created.
SQL>
SQL> create or replace trigger t_bd
2 before delete on c
3 begin
4 my_pkg.g_data.delete;
5 end;
6 /
Trigger created.
SQL>
SQL> create or replace trigger t_adfer
2 after delete on c for each row
3 begin
4 my_pkg.g_data(:old.x) := 'x';
5 end;
6 /
Trigger created.
SQL>
SQL> create or replace trigger t_ad
2 after delete on c
3 declare
4 l_idx binary_integer;
5 CHILD_EXISTS EXCEPTION;
6 PRAGMA EXCEPTION_INIT (CHILD_EXISTS, -2292);
7 begin
8 l_idx := my_pkg.g_data.first;
9 while (l_idx is not null)
10 loop
11 begin
12 delete from p where x = l_idx;
13 exception
14 when child_exists
15 then
16 dbms_output.put_line( 'skipped ' || l_idx );
17 end;
18 l_idx := my_pkg.g_data.next(l_idx);
19 end loop;
20 end;
21 /
Trigger created.
SQL>
SQL> INSERT INTO P VALUES (1);
1 row created.
SQL>
SQL> INSERT INTO P VALUES (2);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (1,2);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (2,1);
1 row created.
SQL>
SQL> INSERT INTO C VALUES (2,2);
1 row created.
SQL>
SQL> COMMIT;
Commit complete.
SQL>
SQL> DELETE FROM C WHERE X = 1 AND Y = 1;
1 row deleted.
SQL>
SQL> select * from P;
X
----------
1
2
SQL>
SQL> select * from C;
X Y
---------- ----------
1 2
2 1
2 2
SQL>
SQL> DELETE FROM C WHERE X = 1 AND Y = 2;
1 row deleted.
SQL>
SQL> select * from P;
X
----------
2
SQL>
SQL> select * from C;
X Y
---------- ----------
2 1
2 2
October 02, 2006 - 9:09 am UTC
but it does not mean I like it :)
Followup:
Rick Davis, October 02, 2006 - 1:12 pm UTC
<<Followup:
but it does not mean I like it :)>>
Ermmm, let's just say we indulge our coders/users with, perhaps, too much "help".
For us, the original Trigger would be "acceptable"; so if there's any word on a fix I'd really appreciate it!
October 02, 2006 - 4:40 pm UTC
won't be any word in a timeframe useful to your implementation is the likely answer....
Useful
Sri, October 03, 2006 - 5:52 am UTC
Ignore ORA-02292
Rick Davis, October 03, 2006 - 11:21 am UTC
<<Followup:
won't be any word in a timeframe useful to your implementation is the likely
answer.... >>
No problem, we've gone with your solution for now. Thanks again!
Please let me now if or when the original Trigger could be used. Regards ...
October 03, 2006 - 4:42 pm UTC
I won't be tracking it that closely to be honest - that won't be a practical request for me to fullfill