Skip to Main Content
  • Questions
  • DML operations in before/after trigger

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Priya.

Asked: December 05, 2016 - 3:56 pm UTC

Last updated: December 08, 2016 - 4:34 pm UTC

Version: 11.1.0.6.0

Viewed 1000+ times

You Asked

SQL> CREATE OR REPLACE package DB_ADMIN.TRI_pkg is
2 type t_dblogin is table of t1%rowtype index by pls_integer;
3 type t_audit is table of audit_tab%rowtype index by pls_integer;
4
5 v_dblogon tri_pkg.T_DBLOGIN;
6 v_audit tri_pkg.t_audit;
7 fire_on_insert boolean := true;
8
9 procedure delete_transaction(p_audit t_audit);
10 end;
11 /

Package created.

SQL>
SQL> CREATE OR REPLACE package body DB_ADMIN.TRI_pkg is
2 procedure delete_transaction(p_audit t_audit) is
3 pragma autonomous_transaction;
4 begin
5
6 INSERT INTO AUDIT_TAB(ACTION,WHO,DTE) VALUES('DELETE','SCOTT',SYSDATE);
7 COMMIT;
8
9 end;
10 end;
11 /

Package body created.

SQL> CREATE OR REPLACE TRIGGER DB_ADMIN.BEFORE_DML_T1
2 BEFORE DELETE OR INSERT OR UPDATE on T1
3 FOR EACH ROW
4
5 DECLARE
6 V_LOGIN_USER NUMBER;
7
8 BEGIN
9 IF DELETING THEN
10 tri_pkg.delete_transaction( tri_pkg.v_audit);
11 raise_application_error (-20002, 'data can not be delet
ed', true);
12 END IF; --CLOSE IF FOR DELETING
13
14 IF INSERTING THEN
15 if audit_pkg.fire_on_insert then
16 :NEW.CREATED := SYSDATE;
17 :NEW.CREATED_BY := USER ;
18 END IF;
19 END IF; -- CLOSE IF FOR INSERTING
20
21 IF UPDATING THEN
22 IF UPPER(:OLD.status) = 'FALSE' THEN
23 RAISE_APPLICATION_ERROR (-20002, 'FALSE RECORDS C
AN NOT BE UPDATED');
24 END IF;
25
26 IF UPPER(:OLD.status) = 'TRUE' THEN
27 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count+1).X := :
old.X;
28 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).Y := :ol
d.Y;
29 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).CREATED :
= :old.CREATED;
30 tri_pkg.v_dblogon(tri_pkg.v_dblogon.count).STATUS :
= 'FALSE';
31 END IF;
32 END IF; --CLOSE IF FOR UPDATING
33
34 END BEFORE_DML_T1;
35 /

Trigger created.

SQL> CREATE OR REPLACE TRIGGER AFTER_DML_T1
2 AFTER UPDATE ON T1
3 BEGIN
4 IF UPDATING THEN
5 TRI_PKG.fire_on_insert := FALSE;
6 forall i in 1..TRI_PKG.v_dblogon.count()
7 INSERT INTO T1 values TRI_PKG.v_dblogon(i);
8 TRI_PKG.fire_on_insert := TRUE;
9 END IF;
10 END AFTER_DML_T1;
11 /

Trigger created.

SQL> select * from t1;

no rows selected

SQL> select * from audit_tab;

no rows selected

SQL> INSERT INTO T1 VALUES(1,99,SYSDATE,'SCOTT','TRUE');


SQL> /

1 row created.

SQL> SELECT * FROM T1;

X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 SCOTT TRUE


SQL> DELETE FROM T1;
DELETE FROM T1
*
ERROR at line 1:
ORA-20002: data can not be deleted
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 7
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'


SQL> SELECT * FROM AUDIT_TAB;

ACTION WHO DTE X Y
---------- -------------------- --------- ---------- ----------
DELETE SCOTT 02-DEC-16

SQL> SELECT * FROM T1;

X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
1 99 02-DEC-16 DB_ADMIN TRUE

SQL>
1 99 02-DEC-16 DB_ADMIN TRUE

SQL> UPDATE T1 SET X=2;

1 row updated.

SQL> SELECT * FROM T1;

X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
2 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE

SQL> UPDATE T1 SET X=3;
UPDATE T1 SET X=3
*
ERROR at line 1:
ORA-20002: FALSE RECORDS CAN NOT BE UPDATED
ORA-06512: at "DB_ADMIN.BEFORE_DML_T1", line 19
ORA-04088: error during execution of trigger 'DB_ADMIN.BEFORE_DML_T1'


SQL> UPDATE T1 SET X=4 WHERE X=2;

1 row updated.

SQL> SELECT * FROM T1;

X Y CREATED CREATED_BY STATUS
---------- ---------- --------- ---------- -------
4 99 02-DEC-16 DB_ADMIN TRUE
1 99 02-DEC-16 DB_ADMIN FALSE
1 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE
2 99 02-DEC-16 DB_ADMIN FALSE

NOTE: DELETE and INSERT works as expected.
NOTE: UPDATE works fine If i try to update when it has single record and false status record.

the issue, if i try to update when it has multiple records. New records are getting inserted for all records in the table which I dont want.

when I wanted to update TRUE status record then new record must be inserted with previous values(status FALSE) and update an existing record value. And it should not insert new records for entire table. Please suggest.

Thank you for your support..!!!

and Chris said...

Thanks for the test case. But please include the table DDL too!

Anyway onto your question...

The array in your package lasts for the duration of your session. So for each update to true rows you another element to the array. Then insert all of these back into the table, including those from the previous update.

To resolve this, delete the array in the after update trigger:

SQL> create table audit_tab (
  2    action varchar2(10),
  3    who varchar2(10),
  4    dte date,
  5    x int,
  6    y int
  7  );

Table created.

SQL> create table t1 (
  2    x number,
  3    y number,
  4    created date,
  5    created_by varchar2(10),
  6    status varchar2(10)
  7  );

Table created.

SQL> CREATE OR REPLACE package TRI_pkg is
  2   type t_dblogin is table of t1%rowtype index by pls_integer;
  3   type t_audit is table of audit_tab%rowtype index by pls_integer;
  4
  5   v_dblogon tri_pkg.T_DBLOGIN;
  6   v_audit tri_pkg.t_audit;
  7   fire_on_insert boolean := true;
  8
  9   procedure delete_transaction(p_audit t_audit);
 10   end;
 11   /

Package created.

SQL> show errors
No errors.
SQL>
SQL> CREATE OR REPLACE package body TRI_pkg is
  2   procedure delete_transaction(p_audit t_audit) is
  3   pragma autonomous_transaction;
  4   begin
  5
  6   INSERT INTO AUDIT_TAB(ACTION,WHO,DTE) VALUES('DELETE','SCOTT',SYSDATE);
  7   COMMIT;
  8
  9   end;
 10   end;
 11   /

Package body created.

SQL> show errors
No errors.
SQL>
SQL> create or replace trigger before_dml_t1 before
  2    delete or
  3    insert or
  4    update on t1 for each row declare v_login_user number;
  5    begin
  6      if deleting then
  7        tri_pkg.delete_transaction ( tri_pkg.v_audit ) ;
  8        raise_application_error ( -20002, 'data can not be deleted', true ) ;
  9      end if; --CLOSE IF FOR DELETING
 10      if inserting then
 11        if tri_pkg.fire_on_insert then
 12          :new.created    := sysdate;
 13          :new.created_by := user ;
 14        end if;
 15      end if; -- CLOSE IF FOR INSERTING
 16      if updating then
 17        if upper ( :old.status ) = 'FALSE' then
 18          raise_application_error ( -20002, 'FALSE RECORDS CAN NOT BE UPDATED' ) ;
 19        end if;
 20        if upper ( :old.status )                                 = 'TRUE' then
 21          tri_pkg.v_dblogon ( tri_pkg.v_dblogon.count+1 ) .x     := :old.x;
 22          tri_pkg.v_dblogon ( tri_pkg.v_dblogon.count ) .y       := :old.y;
 23          tri_pkg.v_dblogon ( tri_pkg.v_dblogon.count ) .created := :old.created;
 24          tri_pkg.v_dblogon ( tri_pkg.v_dblogon.count ) .status  := 'FALSE';
 25        end if;
 26      end if; --CLOSE IF FOR UPDATING
 27    end before_dml_t1;
 28  /

Trigger created.

SQL> show errors
No errors.
SQL>
SQL> CREATE OR REPLACE TRIGGER AFTER_DML_T1
  2   AFTER UPDATE ON T1
  3  BEGIN
  4   IF UPDATING THEN
  5   TRI_PKG.fire_on_insert := FALSE;
  6   forall i in 1..TRI_PKG.v_dblogon.count()
  7   INSERT INTO T1 values TRI_PKG.v_dblogon(i);
  8   TRI_PKG.fire_on_insert := TRUE;
  9   END IF;
 10   tri_pkg.v_dblogon.delete;
 11
 12  END AFTER_DML_T1;
 13  /

Trigger created.

SQL> show errors
No errors.
SQL>
SQL> INSERT INTO T1 VALUES(1,99,SYSDATE,'SCOTT','TRUE');

1 row created.

SQL> /

1 row created.

SQL> SELECT * FROM T1;

         X          Y CREATED   CREATED_BY STATUS
---------- ---------- --------- ---------- ----------
         1         99 07-DEC-16 CHRIS      TRUE
         1         99 07-DEC-16 CHRIS      TRUE

SQL>
SQL> UPDATE T1 SET X=2;

2 rows updated.

SQL> SELECT * FROM T1;

         X          Y CREATED   CREATED_BY STATUS
---------- ---------- --------- ---------- ----------
         2         99 07-DEC-16 CHRIS      TRUE
         2         99 07-DEC-16 CHRIS      TRUE
         1         99 07-DEC-16            FALSE
         1         99 07-DEC-16            FALSE

SQL> UPDATE T1 SET X=3;
UPDATE T1 SET X=3
       *
ERROR at line 1:
ORA-20002: FALSE RECORDS CAN NOT BE UPDATED
ORA-06512: at "CHRIS.BEFORE_DML_T1", line 15
ORA-04088: error during execution of trigger 'CHRIS.BEFORE_DML_T1'


SQL> select * from t1;

         X          Y CREATED   CREATED_BY STATUS
---------- ---------- --------- ---------- ----------
         2         99 07-DEC-16 CHRIS      TRUE
         2         99 07-DEC-16 CHRIS      TRUE
         1         99 07-DEC-16            FALSE
         1         99 07-DEC-16            FALSE

SQL> UPDATE T1 SET X=4 WHERE X=2;

2 rows updated.

SQL> SELECT * FROM T1;

         X          Y CREATED   CREATED_BY STATUS
---------- ---------- --------- ---------- ----------
         4         99 07-DEC-16 CHRIS      TRUE
         4         99 07-DEC-16 CHRIS      TRUE
         1         99 07-DEC-16            FALSE
         1         99 07-DEC-16            FALSE
         2         99 07-DEC-16            FALSE
         2         99 07-DEC-16            FALSE
         2         99 07-DEC-16            FALSE
         2         99 07-DEC-16            FALSE

8 rows selected.


But I'm not really sure what you're trying to do here. What's the business problem you're trying to solve by doing this?

Rating

  (1 rating)

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

Comments

Follow up of DMLTrigger

Priya, December 08, 2016 - 3:17 pm UTC

Thank you. Good solution.
The logic behind this is we will need to update record of status is TRUE only. The FALSE records are history records(previous values) these we will not update. Thanks..!!
Chris Saxon
December 08, 2016 - 4:34 pm UTC

Happy to help

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