Sql Oracle 12c Trigger - sql

I need some help.
I'm trying to create a Trigger that execute a procedure whenever insert, delete or update operations are done on a specific table.
This is the trigger
CREATE OR REPLACE NONEDITIONABLE TRIGGER NQDI.GAV_TRG
AFTER INSERT or UPDATE or DELETE ON D_GAV
FOR EACH ROW
BEGIN
PRC_FILL_D_GAV(:old.report_name);
END;
Unofortunately, since the trigger starts before any commit has been done and I need to read from the same table, it gives me the 'D_GAV table is being modified can't be read' error.
Besides, the FOR EACH ROW makes the trigger start for every record changed, while I want the trigger to start only at the end, when every update, insert or delete has been committed, but I haven't find a way to preserve the :old.report_name while doing this.
I know I could do what I want with an "up and running process", but I'd like to avoid that. Is there any other solution that I'm overlooking?

You want a compound trigger. After each row event you insert the data into an array. And after the statement you loop through the data and call your procedure.
create or replace trigger nqdi.gav_trg
for insert or update or delete on d_gav compound trigger
type type_table_of_gav is table of d_gav%rowtype;
v_gavs type_table_of_gav := type_table_of_gav();
after each row is
begin
v_gavs.extend(1);
v_gavs(v_gavs.count).report_name := coalesce(:old.report_name, :new.report_name);
end after each row;
after statement is
begin
for i in 1 .. v_gavs.count loop
prc_fill_d_gav(v_gavs(i).report_name);
end loop;
end after statement;
end gav_trg;

Related

When does an insert commit with a trigger on the table?

I have a very simple trigger I wrote for a table at work. The problem is when I use 'AFTER INSERT', the trigger locks the table and nothing else can write to it. When I use 'BEFORE INSERT', the trigger works just fine. Both versions of the trigger compile just fine, but only the 'AFTER INSERT' gives me the error when I use a simple insert statement into the 'EVENT_MESSAGE' table. Below is the trigger and error message:
create or replace TRIGGER TWCC_MHE_RERELEASE
AFTER INSERT
ON EVENT_MESSAGE
FOR EACH ROW
DECLARE
m_count number(3);
BEGIN
select count(*) into m_count from event_message where event_id = 7010 and ek_wave_nbr = :new.ek_wave_nbr;
IF :new.event_id = 7010
THEN
insert into TEST_COUNT
(count, wave_nbr, create_date_time)
values
(m_count,:new.ek_wave_nbr, sysdate);
END IF;
END;
A mutating table occurs when a statement causes a trigger to fire and that trigger references the table that caused the trigger. The best way to avoid such problems is to not use triggers :P
Changed the trigger to an after trigger.
Changed it from a row level trigger to a statement level trigger.
Convert to a Compound Trigger.
Modified the structure of the triggers to use a combination of row and statement level triggers.
Made the trigger autonomous with a commit in it.

postgresql triggers possible infinte loop?

I have a table with 3 fields
id, name , value
I want to add a 4th colum calcValue. The calculation of calcValue is based on the filed value in the whole table, changing it in one row can cause changes in all of the others.
I want to write a trigger that updates calcValue everytime there is insert, delete or update in the table.
What i'm worry about is that the trigger itself is going to have an Update command. Will it case another call to this trigger? Will I be stuck with infinte loop?
To describe it better:
CREATE TRIGGER x
AFTER INSERT OR UPDATE OR DELETE
ON a
FOR EACH ROW
EXECUTE PROCEDURE dosomething();
and:
CREATE OR REPLACE FUNCTION dosomething()
RETURNS trigger AS
$BODY$
begin
code + calculation of result...
for row in
QUERY
Loop
Update a set calValue=result where id=...
end loop;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
Will the update of a in dosomething() will cause another invoke of the trigger x? If it does is there a way to handle it so it won't stuck in infinte loop?
My goal is to do dosomething() once per update/insert/delete action of a . I don't want dosomething() to be called again because of the update a in the trigger.
Edit: Since i'm updating in the trigger a diffrent column I can do that:
CREATE TRIGGER x
BEFORE UPDATE OF value ON a
FOR EACH ROW
EXECUTE PROCEDURE dosomething();
this should solve my problem as the trigger updates calcValue and the tigger isn't set invoke on value column. However I would still like to know if there is an answer to my original question... suppose that the trigger would have update the same column.
Another alternative would be to add a when clause, along the lines of:
CREATE TRIGGER x
AFTER INSERT OR UPDATE OR DELETE
ON a
FOR EACH ROW
WHEN NEW IS NULL OR OLD IS NULL OR NEW.value <> OLD.value
EXECUTE PROCEDURE dosomething();
It will only execute the trigger when there's a change in the value field. Therefore when inside the trigger you update calcValue, the trigger is not called again, preventing an infinite "loop".

Invalid character error in Oracle PL/SQL

I am trying out a simple trigger in PL/SQL. The trigger is supposed to maintain a log table. The log will record the system date and the number of rows affected.
This was my code:
CREATE OR REPLACE TRIGGER CREATELOGUPDATE
AFTER DELETE ON TREATMENT_HISTORY
BEGIN
INSERT INTO LOG VALUES(SYSDATE,SQL%ROWCOUNT);
END;
This is just a statement level trigger. It gives me a missing character error at line 2.
I have one more question.
Is SQL%ROWCOUNT correctly used to count the number of rows affected by the last statement?
Changing the code to this:
CREATE OR REPLACE TRIGGER CREATELOGUPDATE
AFTER DELETE ON TREATMENT_HISTORY
DECLARE
rows_number NUMBER;
BEGIN
rows_number:=SQL%ROWCOUNT;
INSERT INTO LOG VALUES(SYSDATE,rows_number);
END;
will make your trigger to be created without an error. However, it will not work as intended as SQL%ROWCOUNT is not right to use here and will permanently return NULL.
One possible solution can be to create a ROW level trigger and increment some number stored in log table for each row affected.

How to create mirror table in oracle by using triggers?

I've created a trigger as follows:
create or replace trigger "PASSENGERS_BACKUP_T1"
after
insert or update or delete on "PASSENGERS"
for each row
begin
if :NEW."P_ID" is NOT null then
INSERT INTO PASSENGERS_BACKUP(
PB_ID,
PB_FIRST_NAME,
PB_LAST_NAME,
PB_STREET_ADDRESS1,
PB_STREET_ADDRESS2,
PB_CITY,
PB_STATE,
PB_POSTAL_CODE,
PB_EMAIL,
PB_PHONE_NUMBER1,
PB_PHONE_NUMBER2,
PB_URL,
PB_CREDIT_LIMIT,
PB_TAGS)
VALUES (
:new.P_ID,
:new.P_FIRST_NAME,
:new.P_LAST_NAME,
:new.P_STREET_ADDRESS1,
:new.P_STREET_ADDRESS2,
:new.P_CITY,
:new.P_STATE,
:new.P_POSTAL_CODE,
:new.P_EMAIL,
:new.PHONE_NUMBER1,
:new.PHONE_NUMBER1,
:new.URL,
:new.CREDIT_LIMIT,
:new.TAGS);
end if;
end;
now, when I update​ an existing row in "passengers" table as per the above trigger another new row is getting added in "passengers_backup" table instead I would like to update the existing row whenever an update is done in "passengers" table rows. As, well If I delete a row in "Passengers" table, if that row exists in 'Passengers_backup' table it should also get deleted. How can I acheive this?
Thanks in advance.
For solving your problem you need to use trigger with corresponding SQL statement for each action: insert, update, delete. As variant you can use something like this (Note, I left only two columns from your example for readability, so modify your trigger as you need):
create or replace trigger "PASSENGERS_BACKUP_TIUD"
after insert or update or delete on "PASSENGER"
for each row
begin
if inserting then
insert into "PASSENGER_BACKUP" (pb_id, pb_first_name)
values (:NEW.pb_id, :NEW.pb_first_name);
elsif updating then
update "PASSENGER_BACKUP"
set pb_id=:NEW.pb_id, pb_first_name=:NEW.pb_first_name
where pb_id=:NEW.pb_id;
elsif deleting then
delete from "PASSENGER_BACKUP"
where pb_id=:OLD.pb_id;
end if;
end;
Also you can see work of this trigger in action on SQL Fiddle.

Oracle Trigger on Insert or Delete or Update

Trying to create an Oracle trigger that runs after a table is updated in any way. I've been googling this all morning and came up with this:
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
delete from gb_transaction where gb_tide = :new.ITST_ITEM_TIDE_CODE;
select TD_IDENTIFIER INTO v_isbn from TD_ITEM_DESCRIPTION where TD_TIDE = :new.ITST_ITEM_TIDE_CODE;
select * INTO v_qty from V_AD_ON_HAND where ITST_ITEM_TIDE_CODE = :new.ITST_ITEM_TIDE_CODE;
insert into gb_transaction(gb_tide, gb_isbn, gb_used_on_hand, gb_new_on_hand)
values(:new.ITST_ITEM_TIDE_CODE, v_isbn, v_qty.USED_ON_HAND, v_qty.NEW_ON_HAND);
END;
/
I'm trying to keep it to a single record per TIDE_CODE in the new table.
V_AD_ON_HAND is a view that pulls an inventory count.
gb_transaction is my new table where I'm logging these events
Comparing it to other peoples code it looks like it should run but I'm getting "Warning: Trigger created with compilation errors."
The problem, I believe is with the :new designations for a delete trigger. There is, after all, no NEW value as the record is expunged. You can only access the :OLD values on delete.
if you section the trigger by operation, you can do this.
CREATE OR REPLACE TRIGGER gb_qty_change
AFTER UPDATE OR INSERT OR DELETE ON F_ITEM_STORE
FOR EACH ROW
DECLARE
v_qty V_AD_ON_HAND%rowtype;
v_isbn TD_ITEM_DESCRIPTION.TD_IDENTIFIER%type;
BEGIN
IF INSERTING or UPDATING then
... insert your existing code
ELSE
... do something similar with the :old values for the deleting case
end if;
END;
/
Incidentally, it is generally helpfull if you tell us WHAT the error is, not just that you had one. If compiling via SQL*Plus script, after the forward slash call to compile the trigger after the "end;" statement, add a line that says:
SHOW ERRORS TRIGGER YOUR_TRIGGER_NAME;