Creating a trigger that updates a table using data from two different tables - sql

OK, so I'm now trying to create a trigger that helps to update a summary table that contains a product id, total sales, and total quantity of items per product. Essentially, I need to create a trigger that fires after the update of an order, when the orderplaced column is set to a value of '1' the summary table will need to be updated by a fired trigger grabbing the data from two other tables which are basket and basketitem to reference the idproduct. I have created the code but for more that i think of it and analyze it, i can't get to an valid compiled trigger that works. I will be adding my code so you can have an idea of what I'm trying to do here. thanks!
create or replace
TRIGGER BB_SALESUM_TRG
AFTER UPDATE OF orderplaced ON BB_BASKET
DECLARE
CURSOR salesum_cur IS
SELECT bi.idproduct as idp, sum(b.total) as tot, sum(b.quantity) as qua,
bi.orderplaced as orpl
FROM bb_basket b, bb_basketitem bi
WHERE b.idbasket = bi.idbasket;
BEGIN
FOR rec_cur IN salesum_cur LOOP
IF rec_cur.orpl = 1 THEN
INSERT INTO bb_sales_sum (idproduct, tot_sales, tot_qty)
VALUES (rec_cur.idp, rec_cur.tot, rec_cur.qua));
END IF;
END LOOP;
END;
I have tried it in different ways, this is the last one I have though. I was also trying with using local variables instead of a cursor but neither way worked, any suggestions are very welcome !
thanks !

If I understand your requirements correctly, the following PL/SQL should point you in the right direction. I was unable to test this:
CREATE OR REPLACE TRIGGER BB_SALESUM_TRG
AFTER UPDATE OF orderplaced ON BB_BASKET
FOR EACH ROW
WHEN (new.orderplaced = 1)
BEGIN
INSERT INTO bb_sales_sum(idproduct, tot_sales, tot_qty number)
SELECT idproduct, sum(b.total), sum(b.quantity)
FROM bb_basket b INNER JOIN bb_basketitem bi
ON b.idbasket = bi.idbasket
WHERE b.idbasket = :new.idbasket
GROUP BY idproduct
END;

Related

SQL - Count number of rows in a table before updating it

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;

What is the right prototype for my SQL code?

I am new in learning SQL and I am trying to apply a trig on those two tables by using SQLcl:
prodc
( PRODC_NAME, DISCOUNTED CHAR(1) DEFAULT 'N',
PK PRIMARY KEY (PRODC_NAME));
peopleOrder
( ORDERID, PRODC_NAME,
PRIMARY KEY (ORDERID, PRODC_NAME),
FOREIGN KEY (PRODC_NAME) REFERENCES PRODC (PRODC_NAME));
I want my trig to do the following:
when I update or insert rows to peopleOrder, the trig should check if a row's product_name has value 'N' in the discounted column that is located in the product table; if it has no 'N' value, it should show an error message.
I tried many ways and the following trig, but when I insert rows, the trig seems to not be working and have no effect on the rows:
CREATE OR REPLACE TRIGGER constraint_1
After UPDATE OR INSERT on peopleOrder
for each row
begin
if not (:new.prodc_name in
(select prodc_name from prodc where discounted = 'N' )) then
RAISE_ERROR(-30001, 'No product can be ordered
!');
END IF;
INSERT INTO peopleOrder VALUES (:new.ORDERID, :new.PRODC_NAME);
END;
/
My insert command is:
INSERT INTO peopleOder (ORDERID, PRODC_NAME)
VALUES (251, 'Puton');
and 'Puton' has value 'N' in the discounted column in the prodc table.
The first thing you need is to understand what a trigger is. It is a piece of code that runs as part of the statement causing it to fire. For this reason you cannot reference the table causing it to fire. Normally that is completely unnecessary anyway. Every row trigger has access to to 2 rows: a copy of the existing data (old) and the resulting data (new). Update having both filled and Insert/Delete just new/old as appropriate for the action. Before statement triggers can modify the new data, after triggers cannot. Those rows allow you to process the columns in the table without DML or select on it. (However you cannot 'scan' the table). At any level a trigger may raise an exception which halts the entire process and the invoking statement. If no exception is raised then processing the invoking proceeds. For a more complete description see PL/SQL Language Reference or the version of Oracle you are running.
In this case your trigger breaks down to a single if
statement.
-- Revised. Orig missed that flag comming from different table.
create or replace trigger constraint_1
before update or insert on peopleOrder
for each row
declare
dis_num integer;
begin
select count(*)
into dis_num
from product
where discounted != 'N'
and product_name = :new.product_name
and rownum < 2;
if dis_num != 0 then
raise_application_error(-20001, 'No discounted product can be ordered!');
end if;
end constraint_1;
-- Orig ==============================================
CREATE OR REPLACE TRIGGER constraint_1
before UPDATE OR INSERT on peopleOrder
for each row
begin
if :new.discounted = 'N' then
RAISE_APPLICATION_ERROR(-20001, 'No discounted product can be ordered')
end if;
END;
/

Creating a Trigger in Oracle

I am trying to create a trigger in Oracle SQLPlus. The trigger deals with two tables:
users{id, name, status}
offerings{id, title, price, userid, status};
I would like that when the user table is updated an the status of a entry is change to 2 that all offerings that the user has made will be changed to i (for inactive)
CREATE OR REPLACE TRIGGER update_offering_status
BEFORE UPDATE ON users
WHEN (new.status = 2)
FOR EACH ROW
DECLARE
Userid INTEGER;
BEGIN
USERID := :old.userid;
UPDATE offering
SET status = 'i'
WHERE userid = old.userid;
END;
I am getting the error ORA-04077: WHEN clause cannot be used with table level triggers. But I am not sure how to do it without a when clause?
That's happening because your WHEN clause is in the wrong place. Put it after the FOR EACH ROW and you should be all set:
CREATE OR REPLACE TRIGGER update_offering_status
BEFORE UPDATE ON users
FOR EACH ROW
WHEN (new.status = 2)
You should be able to put it into IF & END IF instead of WHEN

trigger after on insert, error table mutation upon inserting record

I have created a trigger that will update the Payment table (for bill) when a new record is inserted to the table Enrollment. My trigger is as follows:
CREATE OR REPLACE TRIGGER EnrollFee_trig
AFTER INSERT ON Enrollment
FOR EACH ROW
DECLARE
amount Payment.TotalPrice%TYPE;
id Payment.LearnerID%TYPE;
BEGIN
SELECT SUM(Price) into amount
FROM LearnerEnrollCourse_View
WHERE LearnerID = :NEW.LearnerID
AND Paid = 'N';
SELECT LearnerID into id
FROM Payment
WHERE LearnerID = :NEW.LearnerID
AND PaymentDate IS NULL;
IF SQL%FOUND THEN
UPDATE Payment
SET TotalPrice = amount
WHERE LearnerID = :new.LearnerID
AND PaymentDate IS NULL;
ELSE
INSERT INTO Payment VALUES
(PaymentID_Seq.nextval, :new.LearnerID, '', amount);
END IF;
END;
/
The trigger can be created successfully. But when inserting new record into Enrollment table, there is error saying ' table ENROLLMENT is mutating, trigger/function may not see it'. I want to know more specific about what problem causing this and how can I solve it.
Mutating table exceptions occur when we try to reference the triggering table in a query from within row-level trigger code. See more here
In this instance I suspect ( though I don't know as there is no definition for LearnerEnrollCourse_View ) the problem is caused by this statement :-
SELECT SUM(Price) into amount
FROM LearnerEnrollCourse_View
WHERE LearnerID = :NEW.LearnerID
AND Paid = 'N';
If the LearnerEnrollCourse_view view refers to the Enrollment table, you will get the mutating table error. There are a number of ways round it, moving your trigger code into a statement level trigger and holding the data in package variables is one workaround, in general though, I think using triggers is not the best way to do this, the more triggers you have, the more likely you are to run into this and other problems. Instead, I would have an api package for the enrollment table, and move the trigger code into there.
Good discussion of triggers here

need to write a trigger

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