I have a table COMMANDE and a table REGROUPE. I have that function:
CREATE OR REPLACE PROCEDURE multiplicateur(a NUMBER, taxes NUMBER, c OUT NUMBER)
IS
BEGIN
c := a * taxes ;
END multiplicateur;
/
I'm trying to make a trigger using that founction to update a total price with taxes from a command that can contain more than one item. I tried this but it doesn't want to work:
create or replace TRIGGER MAJ_PRIX_COMMANDE
AFTER INSERT OR UPDATE OR DELETE ON REGROUPE
FOR EACH ROW
DECLARE
resultat NUMBER;
BEGIN
UPDATE COMMANDE
SET COMMANDE.prixTotal = multiplicateur((CAST((SELECT SUM(prixRegroupe)FROM REGROUPE WHERE REGROUPE.numCommande = :NEW.numCommande)AS NUMBER)),1.15,resultat)
WHERE COMMANDE.numCommande = :NEW.numCommande;
END;
Can someone help me?
How about this? I sent this from my iPhone; this code hasn't been tested.
create or replace TRIGGER MAJ_PRIX_COMMANDE
AFTER INSERT OR UPDATE OR DELETE ON REGROUPE
FOR EACH ROW
DECLARE
resultat NUMBER;
l_groupe NUMBER; --will store sum based on numCommande
BEGIN
--retrieve sum of prixRegroupe
SELECT SUM(r.prixRegroupe)
INTO l_groupe
FROM regroupe r
WHERE r.numCommande = :NEW.numCommande;
--call procedure and pass the sum of priRegroupe
multiplicateur(l_groupe, 1.15, resultat);
--use precedures out argument to update commande
UPDATE COMMANDE c
SET c.prixTotal = resultat
WHERE c.numCommande = :NEW.numCommande;
END;
This code won't work. It will result in a mutating trigger error. Within a trigger you cannot query or modify the same table that the trigger is based on until after the trigger has completed execution.
ORA-04091: table name is mutating, trigger/function may not see it
Your problem right now is that you're querying the regroupe table while the trigger is trying to insert/update the same table. This cannot be accomplished.
You'll need to find a way to get the sum of the prixRegroup column some other way. It's late if I think of anything I'll respond tomorrow.
Related
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;
/
Im doing a college project on Oracle SQL and I keep getting the error "table (...) is mutating, trigger/function may not see it" when I try to update a table. My participateTeams table has 3 columns (eventName varchar(), teamId number, teamPlace number).
The Objective here is that when I update the teamPlace of a given team in an event, the place MUST be lower or equal than the number of participant teams in that same event.
My trigger is as follows:
Before update on participateTeams
for each row
Declare participants number;
Begin
select count(*) into participants from participateteams where :New.eventName = participateteams.eventName;
if(:NEW.teamplace>participants) then
RAISE_APPLICATION_ERROR(-20250,'O lugar da equipa é inválido');
end if;
End;
/
From what I've researched it's because I'm trying to read the same table that called the trigger.
I have also have tried exporting the select part of the code into a function, but the issue persists.
The participants in the event are the teams theirselves and not the individuals that form those same teams. Example: If team A, team B and team C participate in an event E, the count for that same event E should be 3.
Any tips? Thank you.
The mutating table error is just a pain. And what you are trying to do is rather tricky. Here is one approach:
Add a column to teams with the count of participants.
Maintain this column with insert/update, and delete triggers on participateteams.
Write a user-defined function to fetch the count for a given team.
Add a check constraint in participateteams using the user-defined function.
The logically correct trigger for what you want to achieve is this :-
CREATE OR REPLACE TRIGGER TRIG_CHK
AFTER INSERT OR UPDATE OF teamPlace ON participateteams
FOR EACH ROW
DECLARE
participants NUMBER;
BEGIN
select count(*) into participants from participateteams where :New.eventName =
participateteams.eventName;
if(:NEW.teamplace>participants) then
RAISE_APPLICATION_ERROR(-20250,'O lugar da equipa é inválido');
end if;
End;
update participateteams set teamPlace = 2 where eventName = 'B';
But when it gives Mutating table error when trying to update table participateteams,to solve this tricky situation , what I did is :-
step1 :Declare the table columns needed in a package header
CREATE OR REPLACE PACKAGE PKG_TEAMS AS
v_eventName participateteams.eventName%type;
v_teamPlace participateteams.teamPlace%type;
end;
step2 : Initialize the variables in a row level trigger
CREATE OR REPLACE TRIGGER TRG_row
AFTER INSERT OR UPDATE OF teamPlace
ON participateteams
FOR EACH ROW
BEGIN
PKG_TEAMS.v_eventName := :NEW.eventName;
PKG_TEAMS.v_teamPlace := :NEW.teamPlace;
END;
step3 :Now ,rather than using :NEW pseudo columns, using globally initialized variables
CREATE OR REPLACE TRIGGER TRG_stmt
AFTER INSERT OR UPDATE OF teamPlace ON participateteams
DECLARE
v_participants NUMBER;
BEGIN
SELECT COUNT(*) INTO v_participants FROM participateteams
WHERE eventName = PKG_TEAMS.v_eventName;
if(PKG_TEAMS.v_teamPlace>v_participants) then
RAISE_APPLICATION_ERROR(-20250,'CANNOT UPDATE,AGAINST RULES');
end if;
End;
I want to create an update trigger for a table called purchases that will update the total cost field and also log a record in the purchases log table to indicate which purchase was updated.
I don't have much experience with creating triggers but so far this is what i've got
CREATE OR REPLACE TRIGGER Update_Purchase_Audit BEFORE
UPDATE ON Purchases
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
WHEN(NEW.p_id>0)
DECLARE total_cost INTEGER;
BEGIN
set total_cost := (:NEW.quantity * :NEW.unitcost);
:NEW.total_cost:=total_cost*0.165;
INSERT INTO Purchase_Log(eventdate, p_id, descrip)
VALUES(SYSDATE, :NEW.p_id, :NEW.product);
END;
/
I get this error when creating the trigger:
trigger created with compilation errors. When I run show errors; I get ORA-00922: missing or invalid option
Any suggestion? Thanks
CREATE OR REPLACE TRIGGER Update_Purchase_Audit
BEFORE UPDATE ON Purchases
FOR EACH ROW
WHEN (NEW.p_id > 0)
DECLARE
total_cost number;
BEGIN
total_cost := :NEW.quantity * :NEW.unitcost;
:NEW.total_cost := total_cost * 0.165; --<< this is ok BEFORE update
-->> Do you have table relations here? THis may need to go AFTER update
-- in separate trigger
INSERT INTO Purchase_Log (eventdate, p_id, descrip)
VALUES(SYSDATE, :NEW.p_id, :NEW.product);
END;
/
Maybe it's quite easy but I don't get ahead. The table B contains two fields: product_no NUMBER and product VARCHAR2. What the trigger should do is to insert a corresponding product description from BP-table in which you also have the fields product_no and product.
E.g. you enter 20 in B.product_no then it should be automatically entered the right product from BP-Table. Oracle version is 11g.
CREATE OR REPLACE TRIGGER A.trigger_on_B
AFTER INSERT OR UPDATE
ON A.B FOR EACH ROW
DECLARE
varProduct varchar(20);
BEGIN
SELECT bp.report_info
into varProduct
from bp,
B
where bp.product_no = B.product_no;
insert into B (product) values(varProduct) ;
exception
when others then NULL;
END;/
You can user pseudo records :new, :old, to make it work:
CREATE OR REPLACE TRIGGER A.trigger_on_B
AFTER INSERT OR UPDATE
ON A.B FOR EACH ROW
DECLARE
varProduct varchar(20);
BEGIN
SELECT bp.report_info
into varProduct
from bp
where bp.product_no = :new.product_no;
insert into B (product) values(varProduct) ;
exception
when others then NULL;
END;
Get rid of the line when others then NULL; this is unappropriated you can't hide any exceptions.
Get rid of your triggers, it is hard to maintenance code with logic in triggers. For more information check this article
I want to write a trigger for a table "TRANSACTION".When a new line is inserted, I want to trigger to update the field "TRANSACTIONID" to the maximum + 1 of all the previous records.
I on't know much about SQL. Can someone help me?
many thanks
This is a really bad idea for a multi-user environment, as it will serialise inserts into the table. The usual approach is to use an Oracle sequence:
create sequence transaction_seq;
create trigger transaction_bir before insert on transaction
for each row
begin
:new.id := transaction_seq.nextval;
end;
To write a trigger based solution that actually got the max current value plus 1, you would need to write a complex 3-trigger solution to avoid the "mutating table" issue. Or you could create a simpler solution using another table to hold the current maximum value like this:
create table transaction_max (current_max_id number);
insert into transaction_max values (0);
create trigger transaction_bir before insert on transaction
for each row
declare
l_current_max_id number;
begin
update transaction_max set current_max_id = current_max_id + 1
returning current_max_id into l_current_max_id;
:new.id := l_current_max_id;
end;
This will avoid the mutating table issue and will serialize (slow down) inserts, so I don't see any advantage of this over using a sequence.
CREATE TRIGGER trigger1 on TransactionTable
INSTEAD OF INSERT
AS
BEGIN
DECLARE #MaxTranId INT
SELECT
#MaxTranId = MAX(TransactionId)
FROM
TransactionTable
INSERT INTO TransactionTable
SELECT
#MaxTranId + 1 ,
RestOfYourInsertedColumnsHere ,
FROM
inserted
END
GO