Call a function within a trigger in pl/sql - sql

I've searched through the Internet for some resource providing me with an example of how to call a function I've created from within a trigger in PL/SQL.
I've made a function called get_balance that looks like this:
create or replace function get_balance(p_custno in number)
return number
as
v_balance account.balance%type;
begin
select balance
into v_balance
from account
where custno = p_custno;
return v_balance;
end;
/
Now I want to call this function from within a trigger to check the balance before a withdrawal. I tried to do it as follows but I think it's totally wrong:
create or replace trigger bifer_withdrawal
before insert on withdrawal
for each row
begin
if get_balance(p_custno) <= amount then
raise_application_error(-20001, 'Not enough money in account!');
end if;
end;
/
Could someone please provide a newbie with an example of how to call a function from within a trigger?

You need to specify the value for p_custno I have used the default NEW alias but see here for trigger information, also René Nyffenegger's has a good explanation of the NEW and OLD usage:
create or replace
trigger bifer_withdrawal
before insert on withdrawal
for each row
begin
if get_balance(:NEW.custno) <= amount
then
raise_application_error(-20001, 'Not enough money in account!');
end if;
end;
/
You will need to specify what AMOUNT is too. If it is a variable then declare it between the FOR EACH ROW and BEGIN statements:
e.g.:
create or replace
trigger bifer_withdrawal
before insert on withdrawal
for each row
declare
c_amount CONSTANT account.balance%TYPE := 5000; -- Whatever limit you need
begin
if get_balance(:NEW.custno) <= c_amount
then
raise_application_error(-20001, 'Not enough money in account!');
end if;
end;
/
You should ask yourself, do you need to call the function?You could easily wrap the cursor into the trigger and save yourself the function call.
Your answer will depend upon issues around whether you want to reuse the function elsewhere etc.I'm not advocating one way over the other but it is something to consider.
Hope it helps...
EDIT: After the two comments below, if AMOUNT is a column in the table WITHDRAWAL then:
create or replace
trigger bifer_withdrawal
before insert on withdrawal
for each row
begin
if get_balance(:NEW.custno) <= :NEW.amount
then
raise_application_error(-20001, 'Not enough money in account!');
end if;
end;
/

Related

Mutating Trigger - SQL

Another one of those mutating trigger errors without an idea of how to fix it. The function counts the number of reviews given and the trigger displays the number of review records:
CREATE OR REPLACE FUNCTION func_no_of_reviews_for_venue (in_venue_id reviews.venue_id%TYPE)
RETURN NUMBER IS
vn_no_of_reviews reviews.review_id%TYPE := 0;
BEGIN
SELECT COUNT(*)
INTO vn_no_of_reviews
FROM reviews;
RETURN vn_no_of_reviews;
END func_no_of_reviews_for_venue;
/
SHOW ERRORS;
-- trigger to get a venue's average rating when a new review has been added
CREATE OR REPLACE TRIGGER trig_get_venue_avg_rating
AFTER INSERT OR UPDATE OF rating ON reviews
FOR EACH ROW
WHEN (NEW.rating IS NOT NULL)
DECLARE
vn_no_reviews NUMBER(6);
vn_avg NUMBER(2,1);
BEGIN
vn_no_reviews := func_no_of_reviews_for_venue(:NEW.venue_id);
DBMS_OUTPUT.PUT_LINE ('Number of reviews: ' || vn_no_reviews);
END trig_age_ck;
/

insert trigger in pl/sql

I cant find the right structure for the following question in pl/sql:
need a trigger on “products table” that will check the price before inserting a new product, product price must not exceed 4000$.
CREATE or REPLACE TRIGGER pro
BEFORE UPDATE OF price
ON products
FOR EACH ROW
declare
pr products.price%type;
BEGIN
if pr < 4000 then
INSERT INTO products VALUES (:old.product_ID,:old.price);
end if;
END;
Please help
Use check constraint instead of trigger:
create table products (price number);
ALTER TABLE PRODUCTS ADD CONSTRAINT check_price CHECK (price < 4000);
Test:
insert into products values (5000) => ERROR
Edit: If you insist on trigger version:
CREATE or REPLACE TRIGGER pro BEFORE insert or UPDATE OF price ON products
FOR EACH ROW
BEGIN
if :new.price > 4000 then
raise_application_error(-20101, 'Price exceeds 4000.');
end if;
END;
The only reason you would bother with a trigger for this instead of a check constraint is to control the error message. And remember that a trigger assumes that the operation is happening, so to stop the operation your tool is to raise an exception.
CREATE OR REPLACE TRIGGER pro
BEFORE INSERT OR UPDATE
ON products
FOR EACH ROW
BEGIN
if :new.price > 4000 then
RAISE_APPLICATION_ERROR(-20001,'Price exceeds maximum permitted value') ;
end if;
END;
if you don't like to raise errors and just keep old values as I think so, use this code:
CREATE or REPLACE TRIGGER pro
BEFORE UPDATE OF price
ON products
FOR EACH ROW
declare
pr products.price%type;
BEGIN
if :new.price > 4000 then
:new.price := :old.price;
:new.product_ID := :old.product_ID;
end if;
END;

Exception in CURSOR or other solution

I have a DB where selling tickets. I have such procedure, where I count all sold money from some race:
CREATE OR REPLACE PROCEDURE Total_money(depart IN RACE.DEPART_PLACE%TYPE,
dest IN RACE.DESTINATION_PLACE%TYPE, total OUT TICKET.PRICE%TYPE)
IS
CURSOR tickets
IS SELECT t.CLIENT_ID, t.PRICE FROM TICKET t JOIN VAGON v ON t.VAGON_ID = v.VAGON_ID
JOIN RACE r ON v.RACE_ID = r.RACE_ID
WHERE r.DEPART_PLACE = depart AND r.DESTINATION_PLACE = dest;
BEGIN
FOR t IN tickets
LOOP
IF t.CLIENT_ID IS NOT NULL THEN
total := total + t.PRICE;
END IF;
END LOOP;
END;
First question: Can I place an exception into CURSOR declaration? Or what can I do, when I pass wrong depart name or destination name of the train? Or these names don't exist in DB. Then it will create an empty cursor. And return 0 money. How to control this?
Second question: After procedure declaration, I run these commands:
DECLARE t TICKET.PRICE%TYPE;
t:=0;
execute total_money('Kyiv', 'Warsaw', t)
But there is an error(PLS-00103 Encountered the symbol...)
First question: How to fix it?
A simple check is just to test that total is non-zero after the loop:
...
END LOOP;
IF total <= 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'Toal zero, invalid arguments?');
END IF;
END;
If the total could legitimately be zero (which seems unlikely here, apart from the client ID check) you could have a counter of a flag and check that:
CREATE ... IS
found BOOLEAN := false;
CURSOR ...
BEGIN
total := 0;
FOR t IN tickets
LOOP
found := true;
IF t.CLIENT_ID IS NOT NULL THEN
total := total + t.PRICE;
END IF;
END LOOP;
IF NOT found THEN
RAISE_APPLICATION_ERROR(-20001, 'No records, invalid arguments?');
END IF;
END;
execute is an SQL*Plus command, so I'm not sure which way you want this to work. You can use an anonymous block like this:
DECLARE
t TICKET.PRICE%TYPE;
BEGIN
total_money('Kyiv', 'Warsaw', t);
-- do something with t
END;
/
Or using an SQL*Plus (or SQL Developer) variable you can do:
variable t number;
execute total_money('Kyiv', 'Warsaw', :t);
print t
I'd change it from a procedure to a function though; declare a total within it, initialise it to zero, and return that instead of having an out parameter. Then you can call it from PL/SQL or from SQL, within a simple select.
And as ElectricLlama points out, you don't need a cursor; and don't need to do this in PL/SQL at all - just use an aggregate sum(). I assume this is an exercise to learn about cursors though?

SQL function, substract variable on another table when insert data

I have a relational database:
Examination (ExaminationID, DetectedDisease, Name)
Uses (ReagentID, ExaminationID, ReagentQuantity)
Reagent (ReagentID, Name, Stock, MinimumPermissibleStock)
Examined (InsuranceNumber, ExaminationID, ExaminationDate, Results, TakenResults?)
I want to create a function that when a new record of "examined" is added the quantity of the reagent that examination uses to be substracted from "reagent.stock". Also when reagent.minimumPermissibleStock is lower than stock to return a warning message.
For the second part of the problem i tried this, but it does not work:
create function warning() returns trigger AS $warning$
begin
if reagent.new.stock < reagent.minimumPermissibleStock then
raise exception 'Probably mistake';
end if;
return new;
end;
$warning$ LANGUAGE plpgsql;
Thanks in advance
EDIT
I tried that one but still it doesnt work:
CREATE FUNCTION log_examination2() RETURNS trigger AS $$
DECLARE
examID integer;
BEGIN
BEGIN
INSERT INTO examination (detecteddisease, ename) VALUES('disease1', 'name') RETURNING examinationID INTO examID;
INSERT INTO Uses VALUES(reagentID, examID, reagentQuantity);
UPDATE Reagent SET Stock = Stock - reagentQuantity WHERE ReagentID = reagentID;
RETURN new;
END;
END;
$$ LANGUAGE plpgsql VOLATILE;
CREATE TRIGGER validate_quantity AFTER UPDATE OF Stock ON Reagent EXECUTE PROCEDURE log_examination2();
Triggers can only operate on the table/view to which they are attached--hence the error (I'm assuming you created the trigger as an AFTER UPDATE on Examined).
To actually solve the problem, what you want to do is create a transaction to represent the entire set of operations, and then attach the above function as a trigger AFTER UPDATE to Reagent.
For example:
CREATE FUNCTION log_examination(int reagentID, int reagentQuantity) AS $$
BEGIN
BEGIN;
examID := INSERT INTO Examination VALUE("disease1", "name") RETURNING ExaminationID;
INSERT INTO Uses VALUE(reagentID, examID, reagentQuantity);
UPDATE Reagent SET Stock = Stock - reagentQuantity WHERE ReagentID = reagentID;
COMMIT;
END;
$$ LANGUAGE plpgsql;
and
CREATE TRIGGER validate_quantity AFTER UPDATE OF Stock ON Reagent EXECUTE PROCEDURE warning();

PL SQL trigger using raise_application_error thows error.

I have a few things of code I need help debugging but I feel that if I can get one of them running i'll be able to get the rest(oh how i hope).
create or replace
trigger minimumwage
before insert or update on Employee
for each row
begin
if :new.Wage < 7.25
then raise_application_error('-20000,Pay is below Texas minimum wage!');
end if;
end;
/
I'm trying to do this on a table ran on my school's server through sqlplus if that helps.
When you're getting an error, it's always helpful to specify what error. There is a syntax error in the raise_application_error call in your trigger. That procedure takes two arguments, a number and a string. You are passing in a single argument that is one long string.
create or replace trigger minimumwage
before insert or update on Employee
for each row
begin
if :new.Wage < 7.25
then
raise_application_error(-20000,'Pay is below Texas minimum wage!');
end if;
end;
should be valid assuming there is a WAGE column in your EMPLOYEE table.
create or replace trigger deny_dec_pu before update of PU on ARTICLE
for each row
declare
erreur_pu exception;
begin
*insert into erreur values ('Operation de MAJ',sysdate);*
-- this intruction is never executec why ?
if (:new.pu < :old.pu) then
raise erreur_pu ;
end if;
exception
when erreur_pu then
Raise_application_error(-20100, 'rrrrrrrr', FALSE);
end;
/