Is before update trigger will fire if same values are updating into a row of column - oracle

I am using before update trigger for each row on table, say emp_table to update one column modifid_date before loading into table. If I am going to update the table with same/existing values of a row, then is this trigger going to fire or not?
condition in trigger:
:new.modifid_dt := sysdate;
Table Values before update: john (name),4867 (id),20-04-2016 (modifid_dt)
Table values now going to update: john (name),4867 (id)

Your trigger will be fired, no matter the values you are using; for example:
SQL> create table testTrigger ( a number)
2 /
Table created.
SQL> CREATE OR REPLACE TRIGGER before_update_trigger
2 before update on testTrigger
3 for each row
4 begin
5 dbms_output.put_line('Trigger fired!');
6 end;
7 /
Trigger created.
SQL> insert into testTrigger values (10);
1 row created.
SQL>
SQL>
SQL> update testTrigger set a = 10;
Trigger fired!
1 row updated.
SQL> update testTrigger set a = 11;
Trigger fired!
1 row updated.
SQL>

If you want avoid "false" firing you should write trigger like this:
create or replace trigger trigger1
before update on tst
for each row
begin
IF :new.t_key != :old.t_key AND ... THEN
dbms_output.put_line('Trigger fired!');
END IF;
end;
But beware of NULL values, of course.

New or existing values - no matter, anyway you'll perform an update so trigger will fire.

Related

Compound Trigger to turn a single row into a multirow

`Basically, i need to create something that when the user insert a value on a table, it turns into a multi insert, he will say the number of rows that will become in a part of table.
But i have 0 ideia from were begin.
My table have COMPMOV - INT: is the number of rows will return.
VLRPROV - NUMBER:
DTVENC - DATE:
PRAZO - INT:
PARCELA - INT:
i started from a another trigger that i created before, but this one i just fill a field that is blank when the user insert.`
CREATE OR replace TRIGGER "TRG_INC_USU_OBSMULTI"
FOR INSERT OR UPDATE
ON AD_OBSMULTI
COMPOUND TRIGGER TYPE R_OBS_TYPE IS RECORD (
OBS_ID AD_OBSMULTI.ID%TYPE,
OBS_IDOBS AD_OBSMULTI.IDOBS%TYPE,
OBS_CODUSU AD_OBSMULTI.CODUSU%TYPE
);
TYPE T_OBS_TYPE IS TABLE OF R_OBS_TYPE INDEX BY PLS_INTEGER;
T_OBS T_OBS_TYPE;
AFTER EACH ROW IS BEGIN
T_OBS (T_OBS.COUNT + 1).OBS_IDOBS := :NEW.IDOBS;
T_OBS (T_OBS.COUNT).OBS_ID := :NEW.ID;
T_OBS (T_OBS.COUNT).OBS_CODUSU := :NEW.CODUSU;
END AFTER EACH ROW;
AFTER STATEMENT IS L_CODUSU AD_OBSMULTI.CODUSU%TYPE;
BEGIN
SELECT
STP_GET_CODUSULOGADO INTO L_CODUSU
FROM
DUAL;
FOR indx IN 1..T_OBS.COUNT
LOOP
IF T_OBS(indx).CODUSU IS NULL THEN
UPDATE
AD_OBSMULTI
SET
CODUSU = L_CODUSU
WHERE
ID = T_OBS(indx).OBS_ID
AND IDOBS = T_OBS(indx).OBS_IDOBS;
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
A trigger can modify the values of the dml statement (insert/update/delete) only. An additional transaction on the same table within a trigger on that table is generally a bad idea.
But here is a workaround.
Create a table
Create a view on that table
Create an instead of trigger on the view
Insert into the view
Example:
CREATE TABLE MAIN_TABLE (id number);
Table MAIN_TABLE created.
CREATE OR REPLACE VIEW MAIN_VIEW AS
SELECT id, 0 as new_rows FROM MAIN_TABLE;
View MAIN_VIEW created.
INSERT INTO main_view (id) values (100);
1 row inserted.
It is possible to insert into the view. Since there is a 1:1 relationship with the main_table this will insert a row in MAIN_TABLE
SELECT COUNT(*) FROM main_table;
COUNT(*)
----------
1
Now create the instead of trigger on MAIN_VIEW
CREATE OR REPLACE TRIGGER main_view_ioi
INSTEAD OF INSERT
ON main_view
FOR EACH ROW
BEGIN
FOR i IN 1 .. :NEW.NEW_ROWS LOOP
INSERT INTO main_table (id) VALUES (i);
END LOOP;
END;
/
Trigger MAIN_VIEW_IOI compiled
INSERT INTO main_view (id,new_rows) values (1,5);
1 row inserted.
Note that this will say "1 row inserted" because only 1 row was inserted in MAIN_VIEW. However, the instead of trigger created 5 rows in MAIN_TABLE.
SELECT * FROM main_table;
ID
----------
100
1
2
3
4
5

Prevent parallel exection of procedure

I have a table trigger, which calls a procedure when the status change from 2 to 3. The procedure check if the whole group of data(group_id) is in status 3 and then perform some actions.
But now I'm facing the problem that when I set the whole group of data in status 3 at the same time, the procedure get called multiple times and perform this actions multiple times. How can I prevent his? For example with locks
Here is my procedure query:
SELECT COUNT(*)
INTO nResult
FROM ticket
WHERE group_id = nGroupId
AND statusid BETWEEN 0 AND 2;
/* If not all tickets of group in status 3, no action required */
IF nResult != 0 THEN
RETURN;
END IF;
And this is my trigger:
IF (:NEW.STATUSID = 3 AND :OLD.STATUSID = 2) THEN
myprocedure(:NEW.group_id);
END IF;
You probably have a row level trigger, that is fired every time a row is updated; for example:
SQL> create table trigger_table(status number);
Table created.
SQL> insert into trigger_table values (1);
1 row created.
SQL> insert into trigger_table values (2);
1 row created.
SQL> insert into trigger_table values (3);
1 row created.
SQL> create trigger update_trigger
2 after update on trigger_table
3 for each row /* ROW LEVEL */
4 begin
5 dbms_output.put_line('change');
6 end;
7 /
Trigger created.
SQL> set serveroutput on
SQL> update trigger_table set status = 1;
change
change
change
3 rows updated.
You need a table level trigger, fired after every update statement:
SQL> create or replace trigger update_trigger
2 after update on trigger_table
3 begin
4 dbms_output.put_line('change');
5 end;
6 /
Trigger created.
SQL> update trigger_table set status = 1;
change
3 rows updated.
Here you find something more.
As rightly observed by Nicholas Krasnov, in this kind of trigger, considering a set of rows and not a single one, you have not the :new or :old values.
A way to get your needs could be the following, but it's a tricky solution and I'd check it carefully before using in a production environment.
You could create a semaphore table to know if you have to fire the trigger or not, then use two triggers, one at row level, BEFORE update, and one at table level, AFTER update; the row level one checks the values and updates the semaphore table while the table level one, fired after the update, reads the semaphore, calls your procedure, if necessary, then resets the semaphore.
For example:
SQL> create table trigger_table(status number);
Table created.
SQL> insert into trigger_table values (1);
1 row created.
SQL> insert into trigger_table values (2);
1 row created.
SQL> insert into trigger_table values (3);
1 row created.
SQL> create table checkChange (fire varchar2(3));
Table created.
SQL> insert into checkChange values ('NO');
1 row created.
SQL> create or replace trigger before_update_trigger
2 before update on trigger_table
3 for each row /* ROW LEVEL */
4 begin
5 if :new.status = 3 and :old.status = 2 then
6 update checkChange set fire = 'YES';
7 end if;
8 end;
9 /
Trigger created.
SQL> create or replace trigger after_update_trigger
2 after update on trigger_table
3 declare
4 vFire varchar2(3);
5 begin
6 select fire
7 into vFire
8 from checkChange;
9 if vFire = 'YES' then
10 dbms_output.put_line('change');
11 update checkChange set fire = 'NO';
12 end if;
13 end;
14 /
Trigger created.
SQL> update trigger_table set status = 2;
3 rows updated.
SQL> update trigger_table set status = 3;
change
3 rows updated.
SQL>

Oracle trigger to update a column value in the same table

I am in the need of a trigger which will fire on any insert or updates on the specific table and while inserting or updating will substitute a credit card number column of the new row to something bogus such as "1111 1111 1111 1111"
Here is what I cooked up but it doesn't seem to work.
CREATE or REPLACE TRIGGER trigger_name AFTER INSERT ON table1
FOR EACH ROW
BEGIN
update table1 set cc_number_field = '11';
END;
I am on Oracle 10 if that matters.
It's much easier to manipulate the incoming :NEW line with a "before" trigger:
CREATE OR REPLACE TRIGGER table1_cc_trigger
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
:NEW.cc_number_field := '1111 1111 1111 1111';
END;
/
Not tested:
CREATE OR REPLACE TRIGGER t
BEFORE
INSERT on ON table1 FOR EACH ROW
begin
:new.cc_number_field = '11';
END;
PL/SQL Reference - Triggers

Concatenate String with a column in trigger

I have a table called TBL_CAS. In that, FLD_ID as auto increment column and another column is called FLD_CAS_CODE. Now I need to add CAS- as a prefix to FLD_ID and Insert into FLD_CAS_CODE. I need to do this in trigger. I was tried with the below code, But the data in not inserting, What is the problem ?
CREATE OR REPLACE TRIGGER TBL_CAS_TRG
BEFORE INSERT ON TBL_CAS
FOR EACH ROW
BEGIN
:NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
END;
I mean `"cas-"+"fld_id"="cas-fld_id"'
You don't need to put TO_CHAR() around things which are already charcater datatypes. But you should cast the numeric identifier (rather than relying on implicit conversion):
:NEW.FLD_CAS_CODE := 'CAS-'||TRIM(TO_CHAR (:NEW.FLD_ID));
which part isn't working exactly? as your trigger seem to work just fine.
SQL> create table TBL_CAS( FLD_ID number, FLD_CAS_CODE varchar2(20));
Table created.
SQL> CREATE OR REPLACE TRIGGER TBL_CAS_TRG
2 BEFORE INSERT ON TBL_CAS
3 FOR EACH ROW
4 BEGIN
5 :NEW.FLD_CAS_CODE := TO_CHAR ('CAS')||'-'||:NEW.FLD_ID;
6 END;
7 /
Trigger created.
SQL> insert into TBL_CAS (fld_id) values (1001);
1 row created.
SQL> select * From TBL_CAS;
FLD_ID FLD_CAS_CODE
---------- --------------------
1001 CAS-1001
SQL>
This will also work fine:
CREATE OR REPLACE TRIGGER TBL_AREA_CODES_TRG
BEFORE INSERT ON TBL_AREA_CODES
FOR EACH ROW
BEGIN
:NEW.OBRM_AREA_CODE := :NEW.STATE_CODE ||'-'||:NEW.DIST_CODE ||'-'||:NEW.CITY_CODE ||'-'||:NEW.AREA_CODE ;
END;

How to query and execute trigger on the same table

Hope someone can help with this. I am new to triggers and I am trying to create a trigger that checks to see if the record being modified has a specific value.
example
I have a table called Filing that has a filing_id and a filing_status, I want to prevent someone from updating or deleting any records in that table has a filing_status="FILED".
so if i have the following
Filing_id Filing_status Val
--------- ------------- ---
0 Filed X
If someone tried to modify Val the trigger should stop it
I have created the following trigger:
CREATE or replace TRIGGER TRG_PREV_FILING
BEFORE DELETE or UPDATE
ON PF.FILING
FOR EACH ROW
declare
rowcnt number;
BEGIN
SELECT COUNT(filing_id) INTO rowcnt FROM PF.FILING
where status = 'FILED'
and filing_id = :new.filing_id;
if (rowcnt > 0)
then
raise_application_error (-20100, 'You can not delete Or Update initial record');
end if;
END;
The problem I am facing is I am getting:ORA-04091 which is "Table Filing is mutating, Trigger/function may not see it"
So basically I can't query on the same table that I am executing the trigger on? Is that the problem in my case and does anyone know a work around this?
I appreciate any help
You do not have to query the table trigger is firing on to be able to do that kind of check. You can get value of a column that is being modified using :old. Here is an example:
SQL> create table filing(
2 status varchar2(31),
3 val number
4 );
Table created
SQL> create or replace trigger TRG_FILING before delete or update on FILING
2 for each row
3 begin
4 if lower(:old.status) = 'filed'
5 then
6 raise_application_error(-20000, 'You cannot delete or modify this record');
7 end if;
8 end;
SQL> /
Trigger created
SQL> insert into FILING values('FILED', null);
1 row inserted
SQL> insert into filing values('OK', 1);
1 row inserted
SQL> commit;
Commit complete
SQL> select *
2 from filing;
STATUS VAL
------------------------------- ----------
FILED
OK 1
SQL> delete
2 from filing
3 where val is null;
ORA-20000: You cannot delete or modify this record
ORA-06512: at "HR.TRG_FILING", line 4
ORA-04088: error during execution of trigger 'HR.TRG_FILING'
The basic point is that you should design you database in a way that the trigger does its validation based on the updated/deleted row. If you have several rows with the same filing_id then you can overwork you database design. Maybe you really only check against the own table in which case you can use :old. But when you have several rows to check (which I assume because you make a count) then you have to use two tables. Here is a suggestion.
create table filing_status (filing_id number, status varchar2(10));
create table filing_content (filing_id number, content_id number, content varchar2(200));
CREATE or replace TRIGGER TRG_PREV_FILING
BEFORE DELETE or UPDATE
ON FILING_content
FOR EACH ROW
declare
rowcnt number;
BEGIN
SELECT COUNT(filing_id) INTO rowcnt FROM FILING_status
where status = 'FILED'
and filing_id = :new.filing_id;
if (rowcnt > 0)
then
raise_application_error (-20100, 'You can not delete Or update filed record');
end if;
END;
/
insert into filing_status values (1, 'FILING');
insert into filing_content values (1, 1, 'foo');
insert into filing_content values (1, 2, 'bar');
insert into filing_status values (1, 'FILED');
update filing_content set content = 'bahr' where filing_id = 1 and content_id = 2;
ERROR at line 1:
ORA-20100: You can not delete Or update filed record
ORA-06512: at "DEMO.TRG_PREV_FILING", line 9
ORA-04088: error during execution of trigger 'DEMO.TRG_PREV_FILING'

Resources