Oracle SQL PLS-00049: bad bind variable ERROR - sql

I'm doing a trigger that when inserting a shirt number verifies if it already exists in the database, I also did a function called 'VERIFYSHIRT' that the trigger calls. When compiling i've received an Oracle SQL PLS-00049: bad bind variable ERROR. I'm almost sure that the code is right, but don't know what i'm doing wrong. If you guys want I can also put the function here, thanks in advance, or any table needed!
CREATE OR REPLACE TRIGGER SHIRT_NUMBER
BEFORE INSERT OR UPDATE ON Player
FOR EACH ROW
DECLARE
shirtNumber NUMBER;
BEGIN
shirtNumber := VERIFYSHIRT(:NEW.CLUB_CLUBID,:NEW.PLAYER_SHIRTNUMBER);
if shirtNumber > 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Shirt number already exists!');
NULL;
END IF;
END;

A trigger that when inserting a shirt number verifies if it already exists in the database shouldn't be created.
This is what a primary key is for, use it!
https://www.techonthenet.com/oracle/primary_keys.php

Related

How to successfully reference another table before insert with a trigger

I'm trying to create a trigger to validate if a new entry in the table registraties (registrations) contains a valid MNR (employee number) but I'm getting stuck on the part where I'm referencing the table medewerkers (employees).
Could someone help me out?
CREATE OR REPLACE TRIGGER t_MNRcontrole
BEFORE INSERT OR UPDATE
ON registraties
DECLARE
MNR_medewerkers number (SELECT MNR FROM MEDEWERKERS);
FOR EACH ROW
BEGIN
IF :new.MNR <> MNR_medewerkers
THEN raise_application_error(-20111, 'Medewerker niet herkend!');
END IF;
END;
Error message received is
ORA-24344: success with compilation error
The PL/SQL assignment operator is :=, or select x into y from z to populate from a SQL query.
FOR EACH ROW is part of the trigger spec, not the PL/SQL code.
If :new.mnr is not present in the parent table, you will get a no_data_found exception, not a mismatched variable.
It's good practice for error messages to include details of what failed.
In programming, we use indentation to indicate code structure.
A fixed version would be something like:
create or replace trigger trg_mnrcontrole
before insert or update on registraties
for each row
declare
mnr_medewerkers medewerkers.mnr%type;
begin
select mw.mnr into mnr_medewerkers
from medewerkers mw
where mw.mnr = :new.mnr;
exception
when no_data_found then
raise_application_error(-20111, 'Medewerker '||:new.mnr||' niet herkend!');
end;
However, we can implement this kind of check better using a foreign key constraint, for example:
alter table registraties add constraint registraties_mw_fk
foreign key (mnr) references medewerkers.mnr;
MNR_medewerkers number (SELECT MNR FROM MEDEWERKERS);
will always fail because its not a NUMBER, unless your table happens to only have one single entry and even then I am not sure PLSQL will allow it to pass.
The more standard case for this would be to first declare the number, then in the codeblock you do a SELECT INTO along with a WHERE clause where you make sure to only pick one specific row from the table. Then you can compare that number with the new one.
If however you are not trying to compare to one specific row, but are instead checking if the entry exists in that table.
BEGIN
SELECT 1
INTO m_variable
FROM table
WHERE MNR = :new.MNR;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
m_variable = 1;
WHEN OTHERS THEN
m_variable = 0;
END;
Declare the m_variable beforehand, and then check if its 0 then report the error.
The too_many_rows is in case there is more than one row in the table with this MNR, and the OTHERS is there for the NO_DATA_FOUND, but I use OTHERS to handle everything else that could happen but probably wont.
Btw this is a code block to be included within the main code block, so between your BEGIN and IF, then just change the IF to check if the variable is 0.

Oracle trigger error ORA-04091

I get an error (ORA-04091: table DBPROJEKT_AKTIENDEPOT.AKTIE is mutating, trigger/function may not see it) when executing my trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
AFTER
INSERT OR UPDATE OF TAGESKURS
OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
bfr number;
Begin
bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
UPDATE AKTIE
SET BILANZ = TAGESKURS - WERT_BEIM_EINKAUF;
IF bfr < -50
THEN
DBMS_OUTPUT.PUT_LINE('ACHTUNG: The value (Nr: '||:new.AKTIEN_NR||') is very low!');
END IF;
END;
I want to check the value "BILANZ" after calculating it, wether it is under -50.
Do you have any idea why this error is thrown?
Thanks for any help!
There are several issues here:
Oracle does not allow you to perform a SELECT/INSERT/UPDATE/DELETE against a table within a row trigger defined on that table or any code called from such a trigger, which is why an error occurred at run time. There are ways to work around this - for example, you can read my answers to this question and this question - but in general you will have to avoid accessing the table on which a row trigger is defined from within the trigger.
The calculation which is being performed in this trigger is what is referred to as business logic and should not be performed in a trigger. Putting logic such as this in a trigger, no matter how convenient it may seem to be, will end up being very confusing to anyone who has to maintain this code because the value of BILANZ is changed where someone who is reading the application code's INSERT or UPDATE statement can't see it. This calculation should be performed in the INSERT or UPDATE statement, not in a trigger. It considered good practice to define a procedure to perform INSERT/UPDATE/DELETE operations on a table so that all such calculations can be captured in one place, instead of being spread out throughout your code base.
Within a BEFORE ROW trigger you can modify the values of the fields in the :NEW row variable to change values before they're written to the database. There are times that this is acceptable, such as when setting columns which track when and by whom a row was last changed, but in general it's considered a bad idea.
Best of luck.
You are modifying the table with the trigger. Use a before update trigger:
CREATE OR REPLACE TRIGGER Aktien_Bilanz_Berechnung
BEFORE INSERT OR UPDATE OF TAGESKURS OR INSERT OR UPDATE OF WERT_BEIM_EINKAUF
ON AKTIE
FOR EACH ROW
DECLARE
v_bfr number;
BEGIN
v_bfr := :new.TAGESKURS - :new.WERT_BEIM_EINKAUF;
:new.BILANZ := v_bfr;
IF v_bfr < -50 THEN
Raise_Application_Error(-20456,'ACHTUNG: The value (Nr: '|| :new.AKTIEN_NR || ') is very low!');
END IF;
END;

How to write a trigger that checks an updated value in Oracle?

For an assignment I need to write an Oracle SQL trigger that prevents the StdBalance column in the STUDENT table from exceeding 500. Normally I would just use a check constraint to enforce this however I am being forced to write a trigger for it. As expected Oracle is throwing a mutating error due to me using SELECT in an update trigger and will not let me update the StdBalance value with anything. Does anyone have any idea how I could work around this? Here is the code for the trigger.
CREATE OR REPLACE TRIGGER Balance_Trigger
BEFORE UPDATE ON STUDENT
FOR EACH ROW
DECLARE
Current_Balance NUMBER;
BEGIN
SELECT :new.StdBalance
INTO Current_Balance
FROM STUDENT
WHERE :new.stdID = StdID;
IF Current_Balance > 500
THEN Raise_Application_error(-20007, 'You cannot exceed an unpaid balance of $500');
end if;
end;
/
show error;
Just use :
Current_Balance := :new.StdBalance;
Instead of
SELECT :new.StdBalance
INTO Current_Balance
FROM STUDENT
WHERE :new.stdID = StdID;
to suppress the mutating error.
P.S. Even if such assignments are used in these cases, as David Faber warned, there's no need to return a value for Current_Balance, :new.StdBalance may be used directly in comparison for IF :new.StdBalance > 500 ....

Error(7,56): PLS-00049: bad bind variable 'RACE.Race_location'

I'm trying to create a trigger that references a table within my sql code. If there is a more simple way than using a trigger please let me know :) But i seem to be getting this error and i can't for the life of me work out why. Everything is named correctly, i have double and tripple checked.
CREATE OR REPLACE TRIGGER RACEDATECHECK
BEFORE INSERT OR UPDATE ON RACE
REFERENCING NEW AS RACE
FOR EACH ROW
DECLARE meet_end_date DATE;
BEGIN
SELECT EndDate INTO meet_end_date FROM MEETING WHERE :RACE."Race_location" =
MEETING."Meeting_Location" AND :RACE.Meeting_start = MEETING.StartDate;
IF :RACE."Race_Date" > meet_end_date
THEN
RAISE_APPLICATION_ERROR(-20000, 'Error, race date must
fit between meeting parameters');
END IF;
END;

PLS-00103, in Trigger

create or replace TRIGGER log_worlds BEFORE UPDATE OR INSERT ON worlds
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD) VALUES(SYSDATE,ac,tab,world);
INSERT INTO worlds(WORLD_NAME) VALUES(world);
END;
Can someone help me with this,error information is about line 14? This trigger is supposed to add new values to the log table and to change the primary key value of the worlds table when my APEX application issues DML against the table.
the error you get suggests some syntax error in your code which I can't immediately find. However I would expect another error becuase Oracle won't allow you do execute DML statements (select, insert, update, delete) inside of a trigger on the same table as the trigger is on. Your trigger is on table worlds, so you are not allowed to insert a record into table worlds inside the trigger.
I think it's this line here:
world:=world||cast(NEW_WORLD.NEXTVAL as VARCHAR2(10));
If you replace it with:
world := world||to_char(NEW_WORLD.NEXTVAL);
it should work.
Btw: directly using a nextval call in an assignment does not work on versions prior to 11.x (not sure about the value of x here).
In 10.x you would need to declare a variable and then use:
SELECT to_char(new_world.nextval)
into world_num;
world := world || world_num;
The immediate syntax error is that assuming NEW_WORLD is a sequence you've created, you would need to do something like
SELECT world ||
cast( new_world.nextval as varchar2(10) )
INTO world
FROM dual;
rather than directly referencing the sequence in your CAST.
It's far from clear to me what your trigger is supposed to be doing though. It is, at a minimum, going to generate an infinite loop. Every INSERT on WORLDS is going to cause the trigger to fire which will generate an INSERT on WORLDS which will cause the trigger to fire, etc. Oracle will eventually raise an error when you exceed the maximum recursion depth. Perhaps you intended this to be a row-level trigger rather than a statement-level trigger that changed the value of :new.world_name? If that's the case, you'd probably want something like
create or replace TRIGGER log_worlds
BEFORE UPDATE OR INSERT ON worlds
FOR EACH ROW
DECLARE
ac VARCHAR2(50);
tab VARCHAR2(50);
world VARCHAR2(50);
BEGIN
IF UPDATING THEN
ac:='Aktualizacja';
END IF;
IF INSERTING THEN
ac:='Nowe';
END IF;
tab:='WORLDS';
world:='world_';
select world || cast(new_world.nextval as varchar2(10)
into world
from dual;
INSERT INTO log(ACTION_DATE,ACTION,TAB_NAME,ADDED_WORLD)
VALUES(SYSDATE,ac,tab,world);
:new.world_name := world;
END;
This assumes that world_name is not actually the primary key. If world_name is the primary key then it would make no sense to modify the primary key value when a row is updated-- you would only want to potentially assign the primary key if you are doing an INSERT.