Create a trigger that updates a column on one table when a column in another table is updated - sql

i have two tables
Order(id, date, note)
and
Delivery(Id, Note, Date)
I want to create a trigger that updates the date in Delivery when the date is updated in Order.
I was thinking to do something like
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE
ON Order
DECLARE
BEGIN
UPDATE Delivery set date = ??? where id = ???
END;
How do I get the date and row id?
thanks

How do i get the date and row id?
Assuming these are columns on your ORDER table called DELIVERY_DATE and ID your trigger should look something like this:
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE ON Order
FOR EACH ROW
BEGIN
if :new.delivery_date != :old.delivery_date
then
UPDATE Delivery d
set d.delivery_date = :new.delivery_date
where d.order_id = :new.id;
end if;
END;
Note the FOR EACH ROW clause: that is necessary to reference values from individual rows. I have used an IF construct to test whether to execute the UPDATE on Delivery. If you have no other logic in your trigger you could write it like this...
CREATE OR REPLACE TRIGGER your_trigger_name
BEFORE UPDATE OF delivery_date ON Order
FOR EACH ROW
BEGIN
UPDATE Delivery d
set d.delivery_date = :new.delivery_date
where d.order_id = :new.id;
END;
I have answered the question you asked but, as an aside, I will point out that your data model is sub-optimal. A properly normalized design would hold DELIVERY_DATE on only one table: DELIVERY seems teh logical place for it.

Use the OLD and NEW bind variables. OLD references the row or column being updated before the change is made; NEW references it after the change.
CREATE OR REPLACE TRIGGER trig1
BEFORE UPDATE
ON order REFERENCING NEW AS new
FOR EACH ROW
BEGIN
UPDATE delivery
SET ddate = :new.ddate
WHERE id = :new.id;
END;
You can modify the REFERENCING clause to give your bind variables different names. You can include OLD as <name> too. Example:
CREATE OR REPLACE TRIGGER trig1
BEFORE UPDATE
ON order REFERENCING OLD AS old_values NEW AS new_values
...
If you don't want to change the default names of "old" and "new", you can leave out the REFERENCING clause completely.

There is an implicit new and old reference in the trigger in the form of:
REFERENCING OLD AS OLD NEW AS NEW
You can write to the :NEW value but not to the :OLD value.
UPDATE Delivery set date = :new.delivery_date where id = :new.id;
CREATE OR REPLACE TRIGGER "BUR_TABLENAME" BEFORE
UPDATE ON "TABLE" FOR EACH ROW
BEGIN
If :new.active_date is not null Then
:new.active_date := TRUNC(:new.active_date);
End If;
END;
Template:
CREATE OR REPLACE TRIGGER TRIGGER_NAME
BEFORE
UPDATE
ON TABLE_NAME
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
DECLARE
V_VARIABLE NUMBER (1);
BEGIN
//Do Stuff;
null;
end;

Whenever there is a need for this kind of trigger, have a good look at your design. Is there really a need for a separate delivery record? Does an order really have more than 1 delivery ?
Triggers seem nice but they do tend to mess things up pretty quickly.

Related

This Oracle trigger has problems

create or replace NONEDITIONABLE TRIGGER SumUpdate AFTER INSERT ON stavkaotpremnice FOR EACH ROW
declare pragma autonomous_transaction;
begin
UPDATE otpremnica a
set a.ukupno=
(SELECT SUM(ukupno)
FROM stavkaotpremnice
WHERE brojotpremnice =: new.brojotpremnice)
WHERE a.brojotpremnice = :new.brojotpremnice;
commit;
end;
This trigger is to sum values of a column called "ukupno" in table stavakaotpremnice then store it in another table otpremnica in a column also called "ukupno".
The trigger check if the id(brojotpremnice) is the same and make the sum.
Brojotpremnice is a foreign key from table otpremnica.
Does anyone know why it is totally ignoring the first entry?
If i put rows in stavkaotpremnica i just count the first entry.
This type of problem can be solved via incremental addition as follows:
UPDATE otpremnica a
set a.ukupno = a.ukupno + :new.ukupno
WHERE a.brojotpremnice = :new.brojotpremnice;
Also, please read about pragma autonomous_transaction. Why it is used? Intentional? If you have no idea, read it. (It separates the transaction)

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 11g Trigger Issue

I'm a newbie to database and trying to create a trigger which will change a char from "Y" to "N" if another table's tuple is set to 0. I have it working somewhat but it is changing all tuples instead of the single one I want. Here is the trigger and code. Any suggestions gratefully received.
create or replace TRIGGER CHANGE_STOCK_FLAG
AFTER UPDATE OF AMOUNT_REMAINING ON PRODUCT
FOR EACH ROW
BEGIN
UPDATE BOOK
SET IN_STOCK = 'N';
END;
Update statement:
UPDATE PRODUCT
SET AMOUNT_REMAINING = 0
WHERE PROD_ID = 5001;
The trigger compiled OK and on update above resets IN_STOCK to "N" on all tuples in the Book TABLE.
Is there a where clause or something I can use?
Try this:
CREATE OR REPLACE TRIGGER CHANGE_STOCK_FLAG
AFTER UPDATE OF AMOUNT_REMAINING ON PRODUCT
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF (:NEW.AMOUNT_REMAINING=0) THEN
UPDATE BOOK SET IN_STOCK = 'N' WHERE PROD_ID=:NEW.PROD_ID;
END IF;
END;

mutating table when writing a trigger

I have been bogged in this problem for quite a long time....Can anyone help me out?
Here is the thing I want to implement:
I have a table A, A has attributes: id, count, total. Here I am required to implement such a trigger: IF the count in table A is updated, the trigger will set the total to 1.
My initial code is like this:
CREATE OR REPLACE TRIGGER tri_A AFTER UPDATE OF count ON A
FOR EACH ROW
BEGIN
UPDATE A SET total = 1 WHERE id = :new.id;
END;
/
The problem with this is the mutating table. When the table is updated, the table will be locked. I searched for the answers, I tried pragma autonomous_transaction, but I got an invalid trigger specification error. And there are other comments saying that we should try to use a combination of triggers to do this....I can't figure out how to do this
Assuming id is the primary key, you don't want to UPDATE the table. You just want to set :new.total. You'll also need to do this in a BEFORE UPDATE trigger not an AFTER UPDATE trigger.
CREATE OR REPLACE TRIGGER tri_A
BEFORE UPDATE OF count ON A
FOR EACH ROW
BEGIN
:new.total := 1;
END;

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;