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

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;

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 ....

Oracle SQL PLS-00049: bad bind variable ERROR

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

What is wrong with my Oracle Trigger?

CREATE OR REPLACE TRIGGER Net_winnings_trigger
AFTER UPDATE OF total_winnings ON Players
FOR EACH ROW
DECLARE
OldTuple OLD
NewTuple NEW
BEGIN
IF(OldTuple.total_winnings > NewTuple.total_winnings)
THEN
UPDATE Players
SET total_winnings = OldTuple.total_winnings
WHERE player_no = NewTuple.player_no;
END IF;
END;
/
I am trying to get a trigger that will only allow the 'total_winnings' field to be updated to a value greater than the current value.
If an update to a smaller value occurs, the trigger should just leave the set the value to the old value (as if the update never occured)
Since you want to override the value that is specified in the UPDATE statement, you'd need to use a BEFORE UPDATE trigger. Something like this
CREATE OR REPLACE TRIGGER Net_winnings_trigger
BEFORE UPDATE OF total_winnings ON Players
FOR EACH ROW
BEGIN
IF(:old.total_winnings > :new.total_winnings)
THEN
:new.total_winnings := :old.total_winnings;
END IF;
END;
But overriding the value specified in an UPDATE statement is a dangerous game. If this is something that shouldn't happen, you really ought to raise an error so that the application can be made aware that there was a problem. Otherwise, you're creating all sorts of potential for the application to make incorrect decisions down the line.
Something like this should work.. although it will be hiding the fact that an update is not taking place if you try to update to a smaller value. To the user, everything will look like it worked but the data will remain unchanged.
CREATE OR REPLACE TRIGGER Net_winnings_trigger
BEFORE UPDATE OF total_winnings
ON Players
FOR EACH ROW
DECLARE
BEGIN
:new.total_winnings := greatest(:old.total_winnings,:new.total_winnings);
END;