declare and call variable in trigger in oracle - oracle

I am trying to declare a variable and accessing it again to update same table for a particular id.(Updating STATUS from 'E' to 'R')
create or replace trigger resume_trgr
before update on temp_jobs
DECLARE
job_status varchar(2);
begin
select STATUS into :job_status from temp_jobs where id=6120;
if :job_status='E'
then
update temp_jobs set STATUS='R' where id=6120;
end if;
end;
/
But it returning error while executing above code. Error code is below:
Trigger resume_trgr compiled
Errors: check compiler log
Error(5,27): PLS-00049: bad bind variable 'STATUS'

In the question you said that the error refers to STATUS, but that doesn't match your posted code You shoudl be getting two errors referring to JOB_STATUS.
That's because it's defined as a local variable, and you're trying to refer to it as a bind variable. It should not have the colon prefix; so this compiles:
create or replace trigger resume_trgr
before update on temp_jobs
declare
job_status temp_jobs.status%type;
begin
select STATUS into job_status from temp_jobs where id=6120;
if job_status='E'
then
update temp_jobs set STATUS='R' where id=6120;
end if;
end;
/
as does this, which skips the variable completely:
create or replace trigger resume_trgr
before update on temp_jobs
begin
update temp_jobs set STATUS='R' where id=6120 and status = 'E';
end;
/
Whether that's a useful, valid or sensible thing to be doing inside a trigger is another matter. It seems like a separate update you should be doing manually before your 'real' update, possibly in a procedure - if you really want to always update the row for that specific ID regardless of whatever else you are actually doing in your 'real' (triggering) update.
If what you are really trying to do is make sure the status changes to R even if the caller doesn't explicitly do that, then you probably want a row-level trigger that sets the pseudorow value, rather than a statement-level trigger with a hard-coded ID.

Error you got is ... well, strange. A few comments:
There's no variable STATUS in your code.
When selecting into a variable, you don't need a colon (i.e. it is not into :job_status but into job_status).
You should rather use VARCHAR2 datatype, not VARCHAR.
That should, probably, be a row-level trigger (so you miss the FOR EACH ROW) and, once you fix everything, you'll hit the mutating table error
Why did you hardcode ID? Is this really going to do something only for that ID?
What you might do is this: first, test case:
SQL> create table temp_jobs
2 (id number,
3 status varchar2(10));
Table created.
SQL> insert into temp_jobs values (1, 'E');
1 row created.
SQL> insert into temp_jobs values (6120, 'E');
1 row created.
SQL> select * from temp_jobs;
ID STATUS
---------- ----------
1 E
6120 E
Trigger: note the differences between your and my code.
SQL> create or replace trigger resume_trgr
2 before update on temp_jobs
3 for each row
4 DECLARE
5 JOB_STATUS VARCHAR2(2);
6 begin
7 :new.status := case when :old.status = 'E' then 'R'
8 else :new.status
9 end;
10 end;
11 /
Trigger created.
Testing:
SQL> update temp_jobs set status = 'Y' where id = 6120;
1 row updated.
SQL> select * from temp_jobs;
ID STATUS
---------- ----------
1 E
6120 R
SQL> update temp_jobs set status = 'Y' where id = 6120;
1 row updated.
SQL> select * from temp_jobs;
ID STATUS
---------- ----------
1 E
6120 Y
SQL>

Related

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

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.

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 create trigger error (bad bind variable)

I at trying to create trigger with the following code.
CREATE OR REPLACE TRIGGER MYTABLE_TRG
BEFORE INSERT ON MYTABLE
FOR EACH ROW
BEGIN
select MYTABLE_SEQ.nextval into :new.id from dual;
END;
I am getting error
Error(2,52): PLS-00049: bad bind variable 'NEW.ID'
Any ideas? Thanks.
It seems like the error code is telling you there's no such column ID in your table...
Somehow your environment is treating your code as SQL instead of a DDL statement. This works for me (running in sqlplus.exe from a command prompt):
SQL> create sequence mytable_seq;
Sequence created.
SQL> create table mytable (id number);
Table created.
SQL> CREATE OR REPLACE TRIGGER MYTABLE_TRG
2 BEFORE INSERT ON MYTABLE
3 FOR EACH ROW
4 BEGIN
5 select MYTABLE_SEQ.nextval into :new.id from dual;
6 END;
7 /
Trigger created.
Note the trailing "/" - this might be important in the application you are compiling this with.
if one would use proper naming convention the spotting of this type
of errors would be much easier ( where proper means using pre- and postfixes )
for generic object names hinting about their purpose better
i.e. something like this would have spotted the correct answer
--START -- CREATE A SEQUENCE
/*
create table "TBL_NAME" (
"TBL_NAME_ID" number(19,0) NOT NULL
, ...
*/
--------------------------------------------------------
-- drop the sequence if it exists
-- select * from user_sequences ;
--------------------------------------------------------
declare
c int;
begin
select count(*) into c from user_sequences
where SEQUENCE_NAME = upper('SEQ_TBL_NAME');
if c = 1 then
execute immediate 'DROP SEQUENCE SEQ_TBL_NAME';
end if;
end;
/
CREATE SEQUENCE "SEQ_TBL_NAME"
MINVALUE 1 MAXVALUE 999999999999999999999999999
INCREMENT BY 1 START WITH 1
CACHE 20 NOORDER NOCYCLE ;
-- CREATE
CREATE OR REPLACE TRIGGER "TRG_TBL_NAME"
BEFORE INSERT
ON "TBL_NAME"
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
tmpVar NUMBER;
BEGIN
tmpVar := 1 ;
SELECT SEQ_TBL_NAME.NEXTVAL INTO tmpVar FROM dual;
:NEW.TBL_NAME_ID := tmpVar;
END TRG_TBL_NAME;
/
ALTER TRIGGER "TRG_TBL_NAME" ENABLE;
-- STOP -- CREATE THE TRIGGER
If you're like me and your code should be working, try dropping the trigger explicitly before you re-create it. Stupid Oracle.

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