trigger to update specific column when insert/update happened in same table - sql

I trying to write a trigger that will update a column when user insert or updates a row, within the same table.
Example:
insert into user(ID, F_NM, L_NM, EMAIL) values ('1', 'John','Doe','john.doe#market.org.com');
after the insert, i want to call: update user set ORG = 'market' where ID = '1'.
create or replace trigger user_change
after insert or update of EMAIL on USER
for each row
declare
NEW_ORG VARCHAR(10);
BEGIN
CASE
when :NEW.EMAIL like '$#market.org.com' then
NEW_ORG := 'market';
........
END CASE;
UPDATE USER set ORG = NEW_ORG where ID = :NEW.ID
END;
Calculating the new ORG work, but I can't get the update statement to work.
I get 'ORA-04091 table USER is mutating, trigger/funtion may not see it', figure its due to me inserting/updating the same record at same time. Tried adding 'pragma autonomous_transaction' and 'commit' to the trigger, the insert/update of fields works but the ORG does not get updated.
Also tried changing to INSTEAD OF INSERT OR UPDATE OF EMAIL but I keep getting 'ORA-04073 column list not valid for this trigger type'
create or replace trigger user_change
instead of insert or update of EMAIL on USER
while i get 'ORA-25002 cannot create instead of triggers on tables'
create or replace trigger user_change
instead of insert on USER

Why not simply turn the trigger to a before trigger, when you can set the value before it is written? This way, you don't need to run a new DML statement on the table, which avoid the "mutating" error.
create or replace trigger user_change
after insert or update of email on user
for each row
begin
if :new.email like '%#market.org.com' then
:new.org := 'market';
end if;
end;

Looks like your org column can be calculated virtual column. In this case it would be better to create user-defined deterministic pl/sql function that returns correct calculated value and add it to your table, for example:
Alter table t add org varchar2(30) generated always as (f_get_org(email))

Related

UPSERT inserts duplicate null entry into table (ORACLE)

I am trying to make an upsert trigger on ORACLE via PL/SQL by checking some examples, i am doing fine, i think it is the last step i should only configure. My requirement is that :
A system that will insert to that field will remain one column always null, so i will read column value from another table, then upsert it with inclusion of that value.
d2c_region_locale_config holds d2c_is_active value, so i firstly read that value regarding to locale condition then trigger inserts or updates to table with addition of this value on active_for_d2c column.(for update i am using locale and country columns as it is shown on where clause, they are not PK but has not null condition)
So i've created this trigger:
CREATE OR REPLACE TRIGGER BL_PIM_LOCALE_COUNTRY
BEFORE INSERT OR UPDATE ON PIM_LOCALE_COUNTRY REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
DECLARE
l_active_for_d2c INTEGER;
BEGIN
if :NEW.active_for_d2c is null then
DELETE from pim_locale_country where active_for_d2c is null;
select distinct(d2c_isactive) into l_active_for_d2c from d2c_region_locale_config where d2c_locale= :NEW.locale;
UPDATE pim_locale_country
SET locale = :NEW.locale, locale_name = :NEW.locale_name,
country = :NEW.country, country_name = :NEW.country_name, isdummy = :NEW.isdummy,
active_for_d2c = l_active_for_d2c, itextpos = :NEW.itextpos, locale_charset = :NEW.locale_charset,
fallback_locale = :NEW.fallback_locale, default_for_lang = :NEW.default_for_lang, opeclang = :NEW.opeclang
where locale = :NEW.locale and country = :NEW.country;
IF ( sql%notfound ) THEN
INSERT INTO PIM_LOCALE_COUNTRY (locale,locale_name,country,country_name,isdummy,active_for_d2c,itextpos,locale_charset,fallback_locale,default_for_lang,opeclang)
VALUES (:NEW.locale, :NEW.locale_name,:NEW.country,:NEW.country_name,:NEW.isdummy,l_active_for_d2c,:NEW.itextpos,:NEW.locale_charset,:NEW.fallback_locale,:NEW.default_for_lang,:NEW.opeclang);
END IF;
end if;
END;
It currently does the job, reads value and inserts or updates the existing locale-country couple for other values. But critical thing is that, table always has one "null" value(Please check screenshot), even that i run delete statement at the beginning on my trigger. So my question would be how to delete, or how to make this approach on trigger side ?
Many thanks for answers!
Trigger before insert doesn't block insert itself, so you insert that record twice. That is, once your trigger done its work (inserted or updated record), oracle will proceed with insert (or update) using values that stand in NEW record of your trigger. If trigger modifies NEW., it will be stored as you changed it, but if trigger inserts something itself, you can get more records.
You can use instead of insert or update triggers, and then oracle will not run its own inserts/updates after trigger finishes.
But more common way for 1-record triggers is to modify fields in NEW, for this case field NEW.d2c_is_active.
It looks like this (possible typos, please check)
CREATE OR REPLACE TRIGGER BL_PIM_LOCALE_COUNTRY
BEFORE INSERT OR UPDATE ON PIM_LOCALE_COUNTRY REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
if :NEW.active_for_d2c is null then
select d2c_isactive
into :NEW.active_for_d2c
from d2c_region_locale_config
where d2c_locale= :NEW.locale and rownum<=1;
end if;
END;

ORACLE SQL: After Update Trigger

Problem Statement:
Create a trigger named trigger_contact_af_update that is triggered whenever the contact table is updated. This trigger will insert the org_name and action into the table contact_log_history after the update of contact details. The action name in the affected log table contact_log_history is After_Update_Contact.
The query I have at the moment is:
CREATE OR REPLACE TRIGGER trigger_contact_af_update
AFTER UPDATE ON contact
FOR EACH ROW
BEGIN
INSERT INTO contact_log_history (org_name, action) Values (:OLD.org_name, 'After_Update_Contact');
END;
But it's not working. Can someone please tell me why?
The table contact_log_history has only two columns.
Using the :OLD variable only works when using a before trigger. I would recommend you to do that in your case.
CREATE OR REPLACE TRIGGER trigger_contact_af_update
BEFORE UPDATE ON contact
FOR EACH ROW
BEGIN
INSERT INTO contact_log_history (org_name, action) Values (:old.org_name, 'After_Update_Contact');
END;

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.

SQL postgresql insert statement

I have a following question, for example I have a following table:
CREATE TABLE "regions" (gid serial PRIMARY KEY,
"__gid" int8,
"name" varchar(20),
"language" varchar(7),
"population" int8);
And I want to insert some records, say one of the values for "name" is - 'B', what sort of code would I have to write to change 'B' to 'English-Speaking'? Is that done with some sort of trigger? So would I have to write a trigger to change the values automatically on insert? Any help greatly appriciated!!!
It's an UPDATE statement which will do what you wish, in this case:
UPDATE regions set name = 'English-Speaking' where name = 'B';
To put this in a function use something like:
CREATE OR REPLACE FUNCTION insert_into_wgs()
RETURNS void AS
$$
BEGIN
UPDATE regions SET name = 'English-Speaking' WHERE name = 'B';
END
$$
LANGUAGE 'pgpsql';
Then you create a trigger to run this function:
CREATE TRIGGER log_update
AFTER UPDATE ON accounts
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE
insert_into_wgs();
Assuming I've guessed what you mean correctly from your description:
You will need a simple BEFORE INSERT OR UPDATE ... FOR EACH ROW EXECUTE trigger to invoke a PL/PgSQL trigger procedure that changes the value of the NEW record and then does a RETURN NEW.
The documentation contains abundant details, and since this is homework I'm not going to provide a complete example. Start with CREATE TRIGGER and PL/pgSQL trigger procedures.

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;