postgresql triggers possible infinte loop? - sql

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".

Related

Oracle trigger exclude conditions

I am trying to create an Oracle trigger
CREATE OR REPLACE TRIGGER ACT_SESSION_AUDIT_TRIGGER
AFTER INSERT OR UPDATE
ON ACT_SESSION
FOR EACH ROW
DECLARE
BEGIN
if (:NEW.PROTOCOL='HTTP' or :NEW.PROTOCOL='SFTP') and :NEW.PRINCIPAL NOT IN ('AAA%', 'BBB%') then
INSERT INTO ACT_SESSION_AUDIT (PRINCIPAL,
CON_START_TIME,
DIS_END_TIME,
ADAPTER_NAME,
ADAPTER_TYPE,
PROTOCOL)
VALUES(:NEW.PRINCIPAL,
:NEW.CON_START_TIME,
:NEW.DIS_END_TIME,
:NEW.ADAPTER_NAME,
:NEW.ADAPTER_TYPE,
:NEW.PROTOCOL);
END if;
END;
However when the PRINCIPAL begins with AAA, it causes the trigger to be invoked. What is the correct way of using NOT IN with partial strings ?
You need LIKEfor the pattern matching in 'AAA%' and 'BBB%'.
IF :new.protocol IN ('HTTP', 'SFTP') AND
:new.principal NOT LIKE 'AAA%' AND
:new.principal NOT LIKE 'BBB%' THEN
...
You can move this to a WHEN cause by the way, to make this part of the trigger declaration part:
CREATE OR REPLACE TRIGGER ACT_SESSION_AUDIT_TRIGGER
AFTER INSERT OR UPDATE
ON act_session
FOR EACH ROW
WHEN (new.protocol IN ('HTTP', 'SFTP') AND
new.principal NOT LIKE 'AAA%' AND
new.principal NOT LIKE 'BBB%')
BEGIN
In theory this is faster, because the trigger won't get invoked, when the criteria is not met. I don't know, if this can really make a difference.

Oracle SQL Triggers - How to read each new row

I'm trying to write an AFTER INSERT trigger that looks somewhat like this:
CREATE OR REPLACE TRIGGER database.table_name
AFTER INSERT
ON database.table_name
REFERENCING NEW AS NEW_ROW
FOR EACH ROW
BEGIN NOT ATOMIC
DECLARE COUNT INTEGER;
/* code and stuff */
END
The problem I'm having is that this trigger is not looping through the inserts to do what I'm asking it to do.
If 30 new rows are inserted, this trigger will only run on the first row, and then end. How do I get it to run the "code and stuff" on all the newly inserted rows?
As written, your code won't compile. Not only is NOT ATOMIC not valid in Oracle, but you have the order of DECLARE, BEGIN, and END wrong in the body of the trigger, and the proposed trigger name would duplicate the name of the table on which the trigger is (trying to be) defined.
Perhaps something like the following would be more likely to succeed:
CREATE OR REPLACE TRIGGER database.table_name_AI
AFTER INSERT
ON database.table_name
REFERENCING NEW AS NEW_ROW
FOR EACH ROW
DECLARE
nCOUNT INTEGER;
BEGIN
NULL; /* code and stuff */
END database.table_name_AI;
See this db<>fiddle

Sql Oracle 12c Trigger

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;

how to write Instead of update? - Trigger

I have table A and there is a column name COL_A.
I want that if someone change the value, lets say from 1 to 'X' (not costant) that the trigger will change it back from 'X' to 1.
SQLite does not support changing the new column values.
The only way to change a column in a trigger would be to run an UPDATE command,
but that would run the trigger again.
What you can do is to prevent changing the column in the first place:
CREATE TRIGGER IF NOT EXISTS prevent_col_a_change
BEFORE UPDATE OF col_a ON a
BEGIN
SELECT RAISE(ABORT, 'COL_A must not be changed');
END;
UPDATE trigger is a good solution for your case. Just set old value to the new value, that will lead to behaviour you want.
For example:
CREATE OR REPLACE TRIGGER orders_before_update
BEFORE UPDATE
ON orders
FOR EACH ROW
BEGIN
:new.CreatedAt:= :old.CreatedAt;
END;

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;