How to declare one variable for each row in trigger? - oracle

I have following trigger:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF :NEW.ALERT is NULL
THEN
dbms_alert.signal('print_update_event','update_message');
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
I would like to change it because it send alert for each row. I would like to send only one alert if more than one row with ALERT column equal NULL was updated and I would like to send one alert for each row with ALERT column NOT equal NULL.
As I understand Oracle variables I can declare local variable in my trigger but this variable will be declared separately for each row so following change has no sense:
create or replace TRIGGER MY_TIGGER_NAME AFTER UPDATE ON MY_TABLE
REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
WHEN ( NEW.STATUS = ANY (10,40,42,44,46,50,60) and OLD.STATUS != NEW.STATUS)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
flag NUMBER(1,0) :=0;
BEGIN
IF :NEW.ALERT is NULL and flag=0
THEN
dbms_alert.signal('print_update_event','update_message');
flag:=1;
ELSE
dbms_alert.signal( :NEW.ALERT,'update_message');
END IF;
commit;
END;
Package variable could be used instead local variable but I think that package variable is bad idea because two triggers can be executed in parallel. Maybe I am wrong.
I attach diagram of trigger which shows what I would like to achieve.
How to do it?

Related

Trying to create a trigger but get error PLS-00103

create or replace trigger newcontract
before insert on contract
declare numcon int;
for each row
begin
select contractcount
into numcon from task
where task.taskid = old.taskid;
if numcon < 3 then
insert into contract values(taskid, workerid, payment);
else
dbms_output.put_line('task is full');
end if;
end;
Gives this cryptic error
Error(1,5): PLS-00103: Encountered the symbol "FOR" when expecting one of the following: begin function pragma procedure subtype type <an identifier> <a double-quoted delimited-identifier> current cursor delete exists prior
The record being inserted into contract should not be inserted if contract count for that task is about 2. So I need to check the value of contractcount for each record being inserted. I use a select statement to get the value, but I get this error.
You have more than one problem here:
declare section(the part of the trigger where you declare your variables) goes after the for each row part
OLD and NEW values are "accessed" like this : :new.column_name and :old.column_name
The :old value in before insert trigger is always null because you are inserting a new value, there is no old value, only new value.
If you want to prevent insert if some value is smaller than 3 then you can do it like this:
create or replace trigger newcontract
before insert on contract
for each row
declare
numcon int;
begin
select contractcount
into numcon
from task
where task.taskid = :new.taskid;
if numcon < 3 then
raise_application_error(-20000, 'Task is full');
end if;
end;
/
Here is a small demo
For more info please do add some more detailed description and some sample data where you show us what kind of data you want to be able to insert and why and what kind of data you do not want to insert and why.

How can I get the inserted primary key value from AFTER INSERT trigger in Oracle?

My Oracle DB has a table DOC_WF_COMM and its primary key is DWFC_ID. Primary key value is based on a sequence called SQ_DOC_WF_COMM.
I have created a row level AFTER INSERT trigger on that table and inside the trigger I need to join the inserted record with some other tables like this:
create or replace TRIGGER TRG_DOC_WF_COMM_AFT_INS AFTER INSERT ON DOC_WF_COMM REFERENCING OLD AS OLD NEW AS NEW FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
L_SUBJECT VARCHAR2(300);
L_BODY CLOB;
L_PNT_CODE VARCHAR(100) := NULL;
L_DR_PRJ_ID NUMBER(12);
L_STR_EMAIL VARCHAR2(120);
L_DWFC_TO_USR_ID VARCHAR2(12);
L_PNT_ID NUMBER(12);
L_PNT_EMAIL_YN VARCHAR(1);
L_PNT_ACTIVE_YN VARCHAR(1);
L_PNT_NOTIFY_YN VARCHAR(1);
BEGIN
IF INSERTING THEN
L_PNT_CODE := 'WFNT_MESSAGE';
SELECT DR_PRJ_ID, STR_EMAIL, DWFC_TO_USR_ID INTO L_DR_PRJ_ID, L_STR_EMAIL, L_DWFC_TO_USR_ID
FROM DOC_WF_COMM
JOIN DOC_WF_USERS ON DWFU_ID = DWFC_DWFU_ID
JOIN DOC_WORKFLOW ON DWF_ID = DWFU_DWF_ID
JOIN DOCUMENT_REF ON DR_ID = DWF_DR_ID
JOIN ST_REGISTER ON STR_ID = DWFU_STR_ID
WHERE DWFC_ID = :NEW.DWFC_ID AND DWFC_RESPONSE IS NULL;
-- SOME QUERIES HERE
END IF;
END;
The trigger is compiled successfully and when I insert record into DOC_WF_COMM table I get this error:
ORA-01403: no data found ORA-06512
The error is :NEW.DWFC_ID in WHERE clause and I have change it to these values:
:OLD.DWFC_ID
SQ_DOC_WF_COMM.NEXTVAL
SQ_DOC_WF_COMM.CURRVAL
But no any luck. Any idea why this error is and how can I resolve it?
The problem is this line in your trigger:
PRAGMA AUTONOMOUS_TRANSACTION;
That means the trigger executes as an isolated transaction in a separate session, which means it cannot see the uncommitted state of any other session. Crucially this includes the session which fires the trigger, so the autonomous transaction cannot see the record you just inserted. Hence, NO_DATA_FOUND.
You haven't posted the whole trigger or explained what you're trying to do, so only you know why you have included the PRAGMA. However, the chances are you don't need it. Remove the PRAGMA (and the COMMIT) and your trigger should work just fine.
If I understood you correctly, create a local variable and put the next sequence value in there. Then it can be referenced throughout the code, always having the same value. Something like this:
declare
l_seq number := my_seq.nextval;
begin
insert into table_a (id, ...) values (l_seq, ...);
update table_b set id = l_seq where ...
select ... into ... from ... where id = l_seq;
end;
I changed the query inside the trigger to this, it is working fine
SELECT STR_PRJ_ID, STR_EMAIL, :NEW.DWFC_TO_USR_ID INTO L_DR_PRJ_ID, L_STR_EMAIL, L_DWFC_TO_USR_ID
FROM DOC_WF_USERS, ST_REGISTER
WHERE :NEW.DWFC_TO_USR_ID = DWFU_US_ID AND DWFU_STR_ID = STR_ID AND DWFU_ID = :NEW.DWFC_DWFU_ID;
Not sure why is that. If anyone can figure out the mistake in the query given in the question, please let me know. Thanks

update in trigger causing recursive loop when call a function

I create a table that has a self relation like this :
create table Organization ( id , name , parent_id)
for easy access i add organization binary code to the table , and add a trigger for update the organization binary code after change the parent.
when I want to update then i make recursive loop and take dead lock from oracle.
I update the code in a separate function and call it at the end of trigger;
I make the loop like this:
Update The Record
Run The trigger
Update The Organization Code
Run The trigger
update The Organization Code
and so on
CREATE OR REPLACE TRIGGER TRG_Core_Organization_before
before insert OR UPDATE on Core_Organization
for each row
declare
-- local variables here
parent_HIERARCHICODE number;
PRAGMA AUTONOMOUS_TRANSACTION;
begin
IF INSERTING OR (UPDATING AND :new.parentid != :old.parentid) OR
(UPDATING AND :old.hierarchicode IS NULL) THEN
Begin
-- get last code in this root
-- RAISE_APPLICATION_ERROR(-20001,'ERROR HIERARCHICODE');
select HIERARCHICODE
into parent_HIERARCHICODE
from Core_Organization cp
where cp.id = :new.parentid;
select NVL(max(HIERARCHICODE) + 1, parent_HIERARCHICODE || '001')
into :new.hierarchicode
from Core_Organization cp
where cp.parentid = :new.parentid;
EXCEPTION
WHEN NO_DATA_FOUND THEN
:new.hierarchicode := cast(parent_HIERARCHICODE || '001' as number);
END;
END IF;
end;

SQL Trigger Bad Bind Error

I'm trying to create a trigger that will fire if the Prog_Type = 'EPISODE'. I am receiving a bad binding error - PLS - 000049. I believe there is something wrong with my DECLARE state
CREATE OR REPLACE TRIGGER Seas_Pk_Trigger
BEFORE INSERT OR UPDATE OF Seas_ID ON Season_Table
FOR EACH ROW
DECLARE
Prog_Type VARCHAR2(7);
BEGIN
IF (:OLD.Prog_Type <> 'EPISODE')
THEN SELECT Seas_ID_Seq.nextval into :new.Seas_ID from dual;
END IF;
END Seas_Pk_Trigger;
/
If you are referencing a column in your table, you do not need to declare the column Prog_Type to reference it with a :OLD or :NEW pseudo-record. If Prog_Type is in your table (which I assume it is), then just omit the declaration:
CREATE OR REPLACE TRIGGER Seas_Pk_Trigger
BEFORE INSERT OR UPDATE OF Seas_ID ON Season_Table
FOR EACH ROW
BEGIN
IF (:OLD.Prog_Type <> 'EPISODE')
THEN SELECT Seas_ID_Seq.nextval into :new.Seas_ID from dual;
END IF;
END Seas_Pk_Trigger;
/
I'm not sure how to interpret your statement that you only want this to fire when Prog_Type = 'EPISODE' as your trigger has a <> to EPISODE clause?

How to get number of rows affected by a statement when inside that statement's trigger

I have a statement level trigger that fires whenever INSERT UPDATE or DELETE operations are performed on a table (called customers). I want to display a message (to DBMS_OUTPUT) containing the number of rows that were inserted/updated/deleted.
I just want one message for each triggering statement, eg
'4 rows were inserted into customers table'.
How can I access the number of rows that are affected by the triggering statement from INSIDE the trigger declaration, ie XXX in the code below:
CREATE OR REPLACE TRIGGER customer_changes_trigger_2
AFTER INSERT OR UPDATE OR DELETE ON customers
DECLARE
v_operation VARCHAR(10);
v_number_rows NUMBER;
BEGIN
v_number := XXX;
IF INSERTING THEN
v_operation := 'inserted';
END IF;
IF UPDATING THEN
v_operation := 'updated';
END IF;
IF DELETING THEN
v_operation := 'deleted';
END IF;
DBMS_OUTPUT.PUT_LINE
(v_number_rows|| ' rows were ' || v_operation || ' from customers.');
END;
Can't find anything in the documentation, any help appreciated!
One way is to use a global variable to track the number of rows as there is no other way to get the row count from a statement level trigger. You would then need three triggers... one statement level to initialise the variable before the statement is run, one row level to add one to the variable for each row, one statement level to use the row count however you wish. First, set up the variable and a few procedures to help it:
create or replace package PKG_ROWCOUNT is
NUMROWS number;
procedure INIT_ROWCOUNT;
procedure ADD_ONE;
function GET_ROWCOUNT
return number;
end PKG_ROWCOUNT;
/
create or replace package body PKG_ROWCOUNT as
procedure INIT_ROWCOUNT is
begin
NUMROWS := 0;
end;
procedure ADD_ONE is
begin
NUMROWS := Nvl(NUMROWS, 0) + 1;
end;
function GET_ROWCOUNT
return number is
begin
return NUMROWS;
end;
end PKG_ROWCOUNT;
/
The first trigger to initialise the variable:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_1
before insert or update or delete
on CUSTOMERS
begin
PKG_ROWCOUNT.INIT_ROWCOUNT;
end;
The second to update per row:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_2
after insert or update or delete
on CUSTOMERS
for each row
begin
PKG_ROWCOUNT.ADD_ONE;
end;
/
The third to display the total:
create or replace trigger CUSTOMER_CHANGES_TRIGGER_3
after insert or update or delete
on CUSTOMERS
begin
Dbms_output.
PUT_LINE(PKG_ROWCOUNT.GET_ROWCOUNT || ' rows were affected.');
end;
I'm not 100$ sure if it's available inside AFTER trigger body, but you can try examining sql%rowcount

Resources