insert trigger in pl/sql - 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;

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;
/

writing exception handler

Im trying to create an exception handler to prevent null values from being inserted. I also want to make it to where if it is a null value being inserted then the original cost will display inside the message instead.
my two tables are..
create table pitem,
(item_id number,
item_cost number(10,2)
);
and..
create table pitem_audit
(item_id number,
message char(80)
);
and heres the procedure..
create or replace procedure update_item_cost
(iItemId INTEGER,
fNewcost NUMBER) AS
fCurCost NUMBER(10,2);
missing_cost EXCEPTION;
BEGIN
select item_cost INTO fCurCost from pitem
where item_id=iItemId;
IF fCurCost IS null THEN
RAISE missing_cost;
ELSE
UPDATE pitem SET item_cost=fNewcost
where item_id=iItemId;
END IF;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
INSERT INTO pitem_audit VALUES (iItemId, 'Invalid Item identifier.');
COMMIT;
WHEN missing_cost THEN
INSERT INTO pitem_audit VALUES (iItemId, 'Item cost is null.');/*<---heres where I want it to use the original cost of the item saying 'Null cost replaced by original cost of whatever the orignal was*\
COMMIT;
WHEN OTHERS THEN
ROLLBACK;
INSERT INTO pitem_audit VALUES (iItemId, 'Miscellaneous error.');
COMMIT;
END update_item_cost;
/
/*THIS IS THE BLOCK USED TO CALL THE UPDATE_ITEM_COST PROCEDURE AND ALSO HOW I ENTER THE VALUES FOR COST*\
DECLARE
item_ident number;
cost number;
BEGIN
item_ident := 10;
cost :='';
update_item_cost(item_ident, cost);
END;
/
My question is how would I go about writing the exception handler to prevent null values from being entered..should I just do an ALTER TABLE statement and add a NOT NULL constraint? Also how would I get the message under the pitem_audit table to display 'Null cost replaced by original value of [original item_cost]'?
thanks in advance :D

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;
/

Call a function within a trigger in pl/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;
/

problem with trigger in oracle

the problem is this :
I implemented a trigger on the table called CLAN_AFFILIATI that increases (if inseriemento) and decreases (in case of cancellation) an attribute (NUMAFFILIATI) of another table called CLAN. what I would do is block the update NUMAFFILIATI of Clan by the user and had thought to r another trigge on CLAN that did this:
trigger on CLAN_AFFILIATI(CLAN VARCHAR,AFFILIATO VARCHAR,RUOLO VARCHAR)
CREATE OR REPLACE TRIGGER "AggiornamentoNumAffiliati"
AFTER INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
CLAN_APPARTENENZA VARCHAR(20);
BEGIN
IF INSERTING THEN
SELECT NOME INTO CLAN_APPARTENENZA
FROM CLAN
WHERE NOME=:new.CLAN;
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI+1
WHERE CLAN_APPARTENENZA=NOME;
ELSE
SELECT NOME INTO CLAN_APPARTENENZA
FROM CLAN
WHERE NOME=:old.CLAN;
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI-1
WHERE CLAN_APPARTENENZA=NOME;
END IF;
END;
trigger on CLAN (NAME VARCHAR ,NUMAFFILIATI INTEGER)
CREATE OR REPLACE TRIGGER "ModificaNumAffiliati"
BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
CONT NUMBER:=0;
BEGIN
IF INSERTING THEN
IF :new.NUMAFFILIATI <> 0 THEN
RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
END IF;
ELSE
SELECT COUNT(*) INTO CONT
FROM CLAN_AFFILIATI
WHERE :old.NOME=CLAN;
IF CONT <> :new.NUMAFFILIATI THEN
RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
END IF;
END IF;
END;
but so I'm doing is reporting an error:
error ORA-04091: Table ANTONIO.CLAN_AFFILIATI is being modified, the trigger / function can not read
ORA-06512: at "ANTONIO.ModificaNumAffiliati", line 10
ORA-04088: error during execution of trigger 'ANTONIO.ModificaNumAffiliati'
ORA-06512: at "ANTONIO.AggiornamentoNumAffiliati", line 12
ORA-04088: error during execution of trigger 'ANTONIO.AggiornamentoNumAffiliati
how can I solve this problem ....
This is propably solution:
I tested it with this sample tables:
CREATE TABLE CLAN_AFFILIATI(CLAN VARCHAR2(100),AFFILIATO VARCHAR2(100),RUOLO VARCHAR2(100));
CREATE TABLE CLAN (NOME VARCHAR2(100) ,NUMAFFILIATI NUMBER(10));
You need this helper package.
CREATE OR REPLACE PACKAGE STORE_NOMES
AS
TYPE record_nomes IS RECORD (
nome VARCHAR2(100),
operation VARCHAR2(100) -- insert or delete
);
TYPE array_type_nomes IS TABLE OF record_nomes INDEX BY BINARY_INTEGER;
g_array_nomes array_type_nomes;
END STORE_NOMES;
/
Trigger on CLAN table:
CREATE OR REPLACE TRIGGER MODIFICANUMAFFILIATI
BEFORE INSERT OR UPDATE OF NUMAFFILIATI ON CLAN
FOR EACH ROW
DECLARE
l_CONT NUMBER:=0;
BEGIN
IF INSERTING THEN
-- prevent inserting <> 0
IF :new.NUMAFFILIATI <> 0 THEN
RAISE_APPLICATION_ERROR(-20016,'NUMERO ERRATO');
END IF;
ELSE
SELECT COUNT(*) INTO l_CONT
FROM CLAN_AFFILIATI
WHERE CLAN = :old.NOME;
IF l_CONT <> :new.NUMAFFILIATI THEN
RAISE_APPLICATION_ERROR(-20017,'NUMERO ERRATO');
END IF;
END IF;
END;
/
Before statement trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_BEFORE_STMT
BEFORE INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
STORE_NOMES.g_array_nomes.DELETE;
END;
/
After statement trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER TRG_CLAN_AFFILIATI_AFTER_STMT
AFTER INSERT OR DELETE
ON CLAN_AFFILIATI
DECLARE
BEGIN
FOR i IN STORE_NOMES.g_array_nomes.FIRST..STORE_NOMES.g_array_nomes.LAST LOOP
IF(STORE_NOMES.g_array_nomes(i).operation = 'INSERTING') THEN
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI+1
WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
ELSIF(STORE_NOMES.g_array_nomes(i).operation = 'DELETING') THEN
UPDATE CLAN
SET NUMAFFILIATI=NUMAFFILIATI-1
WHERE NOME = STORE_NOMES.g_array_nomes(i).NOME;
END IF;
END LOOP;
END;
/
Row Insert/Delete trigger on CLAN_AFFILIATI table:
CREATE OR REPLACE TRIGGER AGGIORNAMENTONUMAFFILIATI
BEFORE INSERT OR DELETE ON CLAN_AFFILIATI
FOR EACH ROW
DECLARE
l_CLAN_APPARTENENZA VARCHAR(20);
BEGIN
IF INSERTING THEN
SELECT NOME INTO l_CLAN_APPARTENENZA
FROM CLAN
WHERE NOME = :new.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :new.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'INSERTING';
ELSE
SELECT NOME INTO l_CLAN_APPARTENENZA
FROM CLAN
WHERE NOME = :old.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.COUNT).nome := :old.CLAN;
STORE_NOMES.g_array_nomes(STORE_NOMES.g_array_nomes.LAST).operation := 'DELETING';
END IF;
END;
/
Now working this (without ORACLE-EXCEPTION):
INSERT INTO CLAN(NOME, NUMAFFILIATI) VALUES('Antonio', 0);
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato1','Ruolo1');
INSERT INTO CLAN_AFFILIATI(CLAN,AFFILIATO,RUOLO) values('Antonio','Affiliato2','Ruolo2');
Change the first trigger "AggiornamentoNumAffiliati" so that it doesn't immediately try to update clan, but stores the name (NOME) in a PL/SQL-Table within a Package; then, you make an AFTER INSERT OR DELETE (but without the FOR EACH ROW clause) trigger that reads the PL/SQL table from the package and updates the CLANs accordingly.
I don't have my developer tools with me, but it looks to me as though your getting yourself into a cyclic dependency issue of sorts. When your CLAN_AFFILIATI trigger is raised, in it you do an update of CLAN which calls the second trigger, which has a select from the CLAN_AFFILIATI table in the ELSE block.
Maybe the before insert (first query), and after insert(second query) have an affect also.
ORA-04091 is also known as a "mutating table" error - basically, row triggers cannot query or alter the table on which the trigger operates.
#Martin's answer is the classic description of how to work around this issue, but it you're on Oracle 11+ you can use a compound trigger to do the same thing.
Share and enjoy.