Create Trigger to reorder a column and modify statement levels - sql

I need to Alter the Product table to have a varchar2(3) reorder column and modify the statement level trigger from the notes to set the product reorder field to “yes” if the quantity on hand is less than twice the product min quantity or if the quantity on hand is less than 10. The value should be “no” otherwise. The trigger is fired after an insert or an update to p_min or p_qoh. Debug by checking the data beforehand, making a change, then checking afterwards.
For this I have
CREATE or REPLACE TRIGGER
TRG_Product_Reorder
AFTER INSERT OR UPDATE of P_min, P_qoh ON lab9_Product
BEGIN
UPDATE lab9_Product SET REORDER = 'yes'
WHERE P_qoh < P_min*2 or p_qoh < 10;
UPDATE lab9_Product SET REORDER = 'no'
WHERE P_qoh >= p_min*2;
END;
/
I get the error:
SQL statement ignored
"REORDER": invalid identifier

I think you might want a before trigger rather than an after trigger. It would be something like this:
CREATE or REPLACE TRIGGER
TRG_Product_Reorder
BEFORE INSERT OR UPDATE of P_min, P_qoh ON
lab9_Product
BEGIN
IF :OLD.P_qoh < :OLD.P_min*2 AND :OLD.p_qoh < 10 THEN
:NEW.REORDER := 'yes';
ELSE :NEW.REORDER := 'no';
END IF;
END;

Related

how to convert a mutating trigger to a stored procedure

I have the following trigger:
CREATE OR REPLACE TRIGGER planning_trig BEFORE
UPDATE OF planned_remediation_date ON evergreen
FOR EACH ROW
DECLARE
planned_remediation_date DATE;
BEGIN
SELECT
planned_remediation_date
INTO planned_remediation_date
FROM
evergreen
WHERE
hostname = :new.hostname
AND instance_name = :new.instance_name
AND product_home = :new.product_home
AND it_service = :new.it_service
AND sw_name = :new.sw_name;
IF
planned_remediation_date IS NOT NULL
AND planned_remediation_date > trunc(sysdate)
THEN
UPDATE evergreen
SET
planning_status = 'planned';
ELSE
UPDATE evergreen
SET
planning_status = 'overdue';
END IF;
END;
/
After an update of the row in my table evergreen I get this error:
ORA-04091: table PTR.EVERGREEN is mutating, trigger/function may not see it
I believe the error comes from the fact that I'm trying to update the very same table the trigger is firing on. I read that the best way to handle updates in the same table are stored procedures but I'm quite new to oracle and don't know how to achieve that. Another possibility I heard is AUTONOMOUS_TRANSACTION Pragma but not sure either if this applies to my case.
A little more background, the underlying table has a composite unique key (hostname,instance_name,product_home,it_service,sw_name) and I want to update an existing column on this table called planning_status based on the updated value of planned_remediation_date. If that value is not null and greater then today then update with planned else overdue.
Expression planned_remediation_date IS NOT NULL is redundant. planned_remediation_date > trunc(sysdate) is never TRUE when planned_remediation_date is NULL.
Your trigger can be written much shorter:
CREATE OR REPLACE TRIGGER planning_trig
BEFORE UPDATE OF planned_remediation_date ON evergreen
FOR EACH ROW
BEGIN
IF :new.planned_remediation_date > trunc(sysdate) THEN
:new.planning_status := 'planned';
ELSE
:new.planning_status := 'overdue';
END IF;
END;
/
I guess you like to modify the updated row, not other rows in that table. Otherwise you would need a procedure or a COMPOUND trigger

How to change from oracle trigger to oracle compound trigger?

I have to change from given Oracle trigger:
CREATE OR REPLACE TRIGGER MY_TRIGGER
AFTER UPDATE OF STATUS ON T_TABLE_A
FOR EACH ROW
BEGIN
UPDATE T_TABLE_B T
SET T.STATUS = :NEW.STATUS
WHERE T.REF_ID = :NEW.ID;
END;
/
to an Oracle compound trigger. Effect must be the same. My approach is now:
CREATE OR REPLACE TRIGGER MY_NEW_TRIGGER
for insert or update on T_TABLE_A
compound trigger
before statement -- STUB
is
begin
null;
end before statement;
before each row
is
begin
end before each row;
after each row -- STUB
is
begin
--IDEA: collect ids of changed records (T_TABLE_A) here >> in a global variable? array?
end after each row;
after statement -- STUB
is
begin
--IDEA: Bulk Update of T_TABLE_B (goal is: update T_TABLE_B.STATUS column; must be the same as T_TABLE_A.STATUS)
end after statement;
end;
/
But as a Java Developer I am very slow to find out the correct syntax of variables, arrays and simple DB scriptings, so any approach is helpful.
Approach where to start is marked as "IDEA".
As I can see you have almost done the half of the job , I amended the code to IDEA part.
I have not included the before statement part and also the triggering clause and all you can adjust according to your need. I just considered for updating the status column in my code.
CREATE OR REPLACE TRIGGER my_trigger
FOR UPDATE OF status ON t_table_a
COMPOUND TRIGGER
-- record type to hold each record updated in t_table_a
-- columns we are intersted in are id and status
TYPE table_a_rec IS RECORD(
id t_table_a.id%TYPE
,status t_table_a.status%TYPE);
--table type based on record to access each record using this
TYPE table_a_row_data_t IS TABLE OF table_a_rec INDEX BY PLS_INTEGER;
-- global variable for the compound trigger
g_row_level_data table_a_row_data_t;
AFTER EACH ROW IS
BEGIN
-- IDEA: collect ids of changed records (T_TABLE_A) here >> in a global variable? array?
g_row_level_data(g_row_level_data.count + 1).id := :new.id;
g_row_level_data(g_row_level_data.count).status := :new.status;
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
--IDEA: Bulk Update of T_TABLE_B (goal is: update T_TABLE_B.STATUS column; must be the same as T_TABLE_A.STATUS)
FORALL i IN 1 .. g_row_level_data.count
UPDATE t_table_b t
SET t.status = g_row_level_data(i).status
WHERE t.ref_id = g_row_level_data(i).id;
END AFTER STATEMENT;
END my_trigger;
/

PL/SQL ORACLE: Trigger updates on delete

I am trying to make a trigger that increases the booked value and decreases the available value whenever new record is inserted inside the table ticket_price. If a record is deleted, I want it to decrease the booked value and increase the available value.
Although I am able to successfully make the trigger work for INSERT, I am unable to do the same for updating the values on deletion of a record.T his is the error I get whenever I try to delete a record
ORA-01403: no data found
ORA-06512: at "K.CAL", line 6
ORA-04088: error during execution of trigger 'K.CAL'
Just to clarify, I am updating values in another table, not the same table I am deleting!
Here is my code for the trigger:
CREATE OR REPLACE TRIGGER cal
BEFORE INSERT OR DELETE ON TICKET_PRICE FOR EACH ROW
DECLARE
V_TICKET TICKET_PRICE.TICKETPRICE%TYPE;
V_BOOKED FLIGHTSEAT.BOOKED_SEATS%TYPE;
V_AVAILABLE FLIGHTSEAT.AVAILABLE_SEATS%TYPE;
BEGIN
SELECT BOOKED_SEATS,AVAILABLE_SEATS
INTO V_BOOKED,V_AVAILABLE
FROM FLIGHTSEAT
WHERE SEAT_ID=:NEW.SEAT_ID;
IF INSERTING THEN
V_BOOKED:=V_BOOKED+1;
V_AVAILABLE:=V_AVAILABLE-1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=:NEW.SEAT_ID;
ELSIF DELETING THEN
V_BOOKED:=V_BOOKED-1;
V_AVAILABLE:=V_AVAILABLE+1;
UPDATE FLIGHTSEAT
SET BOOKED_SEATS=V_BOOKED, AVAILABLE_SEATS=V_AVAILABLE
WHERE SEAT_ID=1;
END IF;
END;
You have correctly surmised that :new.seat is not available on the update for a delete. But neither is it available for the select and ow could you know sea_id=1 was need to be updated? For reference to Deleted row data use :Old.column name; is this case use :old_seat_id for both select and update.
But you don't need the select at all. Note: Further you have an implied assumption that seat_id is unique. I'll accept that below.
create or replace trigger cal
before insert or delete on ticket_price
for each row
declare
v_seat_id flightseat.seat_id%type;
v_booked flightseat.booked_seats%type;
begin
if INSERTING then
v_booked := 1;
v_seat_id := :new.seat_id;
else
v_booked := -1;
v_seat_id := :old.seat_id;
end if;
update flightseat
set booked_seats=booked_seats+v_booked
, available_seats=available_seats-v_booked
where seat_id = v_seat_id;
end;

Wondering why my SQL statement is ignoring my SET symbol

This is my SQL trigger and it is suppose to change the reorder value to Y or N depending if ON_HAND is less than or greater than the MINIMUM. The problem is it's ignoring the set statement completely. Do I have to arrange these differently?
CREATE OR REPLACE TRIGGER TRG_REORDER
AFTER UPDATE OF ON_HAND, MINIMUM ON PART
BEGIN
IF ON_HAND <= MINIMUM THEN
SET REORDER = 'Y';
ELSE ON_HAND > MINIMUM
SET REORDER = 'N';
END IF;
END;
Trigger bodies use PL/SQL syntax, not SQL. So for assignment you need := not set.
Your code has some other syntax errors.
You need to refer to the :new values of the columns.
You don't need a condition in the ELSE clause.
In order to modify the values of columns in a trigger, it must be a BEFORE trigger and fire FOR EACH ROW.
So fixing all that, this should now work for you (caveat: untested code).
CREATE OR REPLACE TRIGGER TRG_REORDER
before UPDATE OF ON_HAND, MINIMUM ON PART
for each row
BEGIN
IF :new.ON_HAND <= :new.MINIMUM THEN
:new.REORDER = 'Y';
ELSE
:new.REORDER = 'N';
END IF;
END;
I don't know Oracle triggers but I suspect you want a BEFORE trigger and an assignment that looks something like this.
:new.REORDER := case when :new.ON_HAND <= :new.MINIMUM then 'Y' else 'N' end;
Check here to get started:
http://www.techonthenet.com/oracle/triggers/before_update.php

PL/SQL Conditional statement using two tables within trigger

I'm working on a simple trigger that updates the Quantity on Hand of a product when adding a new invoice line.
I got that working fine; but I starting thinking that in a practical application it would be prudent to verify that the QOH is sufficient before allowing the update. I'm sure I could write a separate trigger for that, but I'd like to see if it's possible to join two tables for a conditional statement within a trigger.
This is as far as I've gotten; adding a SELECT statement anywhere in there causes all hell to break loose, so I'm a little stumped how I can declare the PRODUCT.P_QOH before calling the conditional.
CREATE OR REPLACE TRIGGER trg_prod_QOH_on_line_add
BEFORE INSERT ON LINE
FOR EACH ROW
BEGIN
IF :NEW.LINE_UNITS > PRODUCT.P_QOH THEN
RAISE_APPLICATION_ERROR(-20202, 'Insufficient quantity on hand');
ELSE
UPDATE PRODUCT
SET P_QOH = P+QOH - :NEW.LINE_UNITS;
WHERE PRODUC.P_CODE = :NEW.P_CODE;
END IF;
END;
/
This isn't a major problem for me, as I said there is probably a different way; I'm just starting to learn this stuff and would like to see what's possible. Thanks for your help.
You get into dangerous territory trying to enforce rules like this via triggers. The solution you are asking for is:
create or replace trigger trg_prod_qoh_on_line_add
before insert on line
for each row
declare
v_qoh product.p_qoh%type;
begin
select p_qoh
into v_qoh
from product
where product.p_code = :new.p_code;
if :new.line_units > v_qoh then
raise_application_error(-20202, 'Insufficient quantity on hand');
else
update product
set p_qoh = p_qoh - :new.line_units
where product.p_code = :new.p_code;
end if;
end;
However this is not a safe solution in a system with more than one concurrent user. Suppose product 'X' has p_qoh=10 and then 2 users do this:
user1> insert into line (p_code, line_units) values ('X', 7);
user2> insert into line (p_code, line_units) values ('X', 8);
user1> commit;
user2> commit;
Both sessions will see that 'X' has p_qoh = 10 so both will succeed and product.p_qoh will end up as -5. All is corrupt!
The safe solution would be to create a check constraint on product:
alter table product add constraint prod_qoh_chk check (p_qoh >= 0);
Now your trigger can be simply:
create or replace trigger trg_prod_qoh_on_line_add
before insert on line
for each row
begin
update product
set p_qoh = p+qoh - :new.line_units;
where produc.p_code = :new.p_code;
end;
This would raise a less friendly error message like:
ORA-02290: check constraint (MYSCHEMA.PROD_QOH_CHECK) violated
You can trap this in your trigger and give the message you want:
create or replace trigger trg_prod_qoh_on_line_add
before insert on line
for each row
begin
update product
set p_qoh = p+qoh - :new.line_units;
where produc.p_code = :new.p_code;
exception
when others then
if sqlerrm like 'ORA-02291:%(MYSCHEMA.PROD_QOH_CHECK)%' then
raise_application_error(-20202,'Insufficient quantity on hand');
else
raise;
end if;
end;
Now if we re-run the 2 user scenario above:
user1> insert into line (p_code, line_units) values ('X', 7);
user2> insert into line (p_code, line_units) values ('X', 8);
user1> commit;
At this point user2's insert fails with the error message:
ORA-20202: Insufficient quantity on hand