PL/SQL Trigger to check value before insert statement - oracle

I am new to PL/SQL and I have an issue regarding a trigger I am trying to implement.
The triggers purpose is to check a monetary value before it's inserted into the table to see if someone made a mistake during inserting. If they have, they will be given a message stating the value is incorrect. The values are in the billions so for now I am just checking if the value entered is above 10000 or not.
The trigger I currently have is;
CREATE TRIGGER Check_Value
BEFORE INSERT OR UPDATE OF "Potential Annual Value By 2026" ON AIPOTENTIALVALUEFORHEALTHCARE
BEGIN
IF (NEW."Potential Annual Value By 2026" < 10000.00) THEN
DBMS_OUTPUT.put_line('Value typed was incorrect');
ELSIF (NEW."Potential Annual Value By 2026" >= 10000.00) THEN
INSERT INTO AIPOTENTIALVALUEFORHEALTHCARE VALUES(NEW.ValueID, NEW.ApplicationID, NEW."Application Name", NEW.KeyDriverForAdoptionID, NEW."KeyDriverDescription", NEW."Potential Annual Value By 2026");
END IF;
END;
This will not work due to an error:
PLS-00201: identifier NEW.'Potential Annual Value By 2026' must be declared
My guess is that I have set the trigger incorrectly and that it doesn't know which value to check when it runs the trigger. From some research, I tried to use .NEW to pass the values of the statement into the trigger however I am not sure if this is the correct implementation.
I had tried the method already posted;
CREATE TRIGGER Check_Value
BEFORE INSERT OR UPDATE OF "Potential Annual Value By 2026" ON AIPOTENTIALVALUEFORHEALTHCARE
BEGIN
IF (:NEW."Potential Annual Value By 2026" < 10000.00) THEN
DBMS_OUTPUT.put_line('Value typed was incorrect');
ELSIF (:NEW."Potential Annual Value By 2026" >= 10000.00) THEN
INSERT INTO AIPOTENTIALVALUEFORHEALTHCARE VALUES
(:NEW.ValueID, :NEW.ApplicationID, :NEW."Application Name",
:NEW.KeyDriverForAdoptionID, :NEW."KeyDriverDescription",
:NEW."Potential Annual Value By 2026");
END IF;
END;
and recieved a different error:
ORA-04082: NEW or OLD references not allowed in table level triggers
04082. 00000 - "NEW or OLD references not allowed in table level triggers"
*Cause: The trigger is accessing "new" or "old" values in a table trigger.
*Action: Remove any new or old references.
If this error is stating I can't use NEW references in a table level trigger, how would I be able to verify the contents of the insert statement before it is committed?

You are missing colons before the NEW keywords and the FOR EACH ROW clause. Also you do not need to (and must not) re-issue the INSERT within the trigger, it will happen anyway (if no error is raised):
CREATE TRIGGER Check_Value
BEFORE INSERT OR UPDATE OF "Potential Annual Value By 2026" ON AIPOTENTIALVALUEFORHEALTHCARE
FOR EACH ROW
BEGIN
IF (:NEW."Potential Annual Value By 2026" < 10000.00) THEN
RAISE_APPLICATION_ERROR(-20001, 'Value typed was incorrect');
END IF;
END;
I'm sure this is just a training example, but DBMS_OUTPUT.PUT_LINE is not a suitable method for raising errors to users as its output can only be seen when using develpper tools like SQL Developer. Use RAISE_APPLICATION_ERROR. Also it doesn't actually raise an exception, so it won't prevent the insert at all.
In fact, this check might be better done with a CHECK constraint - assuming the column value must never be under 10000:
ALTER TABLE AIPOTENTIALVALUEFORHEALTHCARE
ADD CONSTRAINT AIPOTENTIALVALUEFORHEALTHCARE_CHK_VALUE
CHECK ("Potential Annual Value By 2026" >= 10000);

Related

Error: can't create trigger properly (ORA-24344: success with compilation error ORA-06512). Oracle SQL

Oracle APEX. I want to create trigger: if user deletes a row where ENDDATE is null the row won't be deleted overwise it will. This is my script:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (OLD.JOBNO, OLD.EMPNO, OLD.STARTDATE, OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
But I have ORA-24344 error. Can you explain why and what should I do to fix it?
Your trigger attempts to re-insert the row if the END_DATE is null. This won't work (you'll get the notorious mutating table error). But anyway, if you want to prevent deletion of the row it's simpler and clearer to simply do that:
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF :OLD.ENDDATE IS NULL THEN
raise_application_error(-20000, 'Cannot delete a row when ENDDATE is null');
END IF;
END CHECK_NOT_NULL_;
This fails the action and tells the user why their action was refused. Silently undoing a user's action is bad practice, because it's mystifying, and mystified users are unhappy and often angry users.
Precede all olds with a colon :, i.e.
CREATE OR REPLACE TRIGGER CHECK_NOT_NULL_
BEFORE DELETE ON CAREER
FOR EACH ROW
BEGIN
IF(:OLD.ENDDATE IS NULL)
INSERT INTO CAREER VALUES (:OLD.JOBNO, :OLD.EMPNO, :OLD.STARTDATE, :OLD.ENDDATE);
END IF;
END CHECK_NOT_NULL_;
Also, I'd suggest you to name all columns you're inserting into, e.g.
insert into career (jobno, empno, startdate, enddate)
values (:old.jobno, :old.empno, :old.startdate, :old.enddate);

SIGNAL SQLSTATE Inside Trigger Failing

I have a Product table with 4 columns. 2 columns are price. If the ListPrice column is updated to below a specified amount (StandardCost * 1.2) then the update should fail and the old ListPrice should remain. I am attempting to use a SIGNAL SQLSTATE error to prevent the update from occurring if the criteria are met.
I've been combing Google and tried various variations in the syntax, but I keep hitting the following error while compiling my trigger - "PLS-00103 - Encountered the symbol 'SQLSTATE' when expecting one of the following: := , ( # %"
Any help is greatly appreciated.
CREATE OR REPLACE TRIGGER Product_Price_Check
BEFORE INSERT OR UPDATE OF ListPrice ON Product
FOR EACH ROW
DECLARE
min_price NUMBER(10, 2);
new_price NUMBER(10, 2);
BEGIN
min_price := (:OLD.StandardCost*1.2);
new_price := (:NEW.ListPrice);
IF (new_price < min_price) THEN
-- Rolls back an explicit or implicit transaction to the beginning of the transaction
dbms_output.put_line('the price can’t be below ' || TO_CHAR(min_price));
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Insert/update failed';
END IF;
END;
As mustaccio said, you're mixing MySQL syntax with an Oracle trigger. You want raise_application_error:
BEGIN
IF :NEW.ListPrice < (:OLD.StandardCost*1.2) THEN
raise_application_error(-20001,
'the price can’t be below ' || TO_CHAR(:OLD.StandardCost*1.2));
END IF;
END;
/
This won't roll back the transaction, just the update statement. The caller will receive the exception and decide how to handle it - whether to try again, roll back, or commit any other changes already made.
This assumes the old standard cost cannot be null. You might also want to specify a format model for the to_char().
Also don't rely on dbms_output for informing the caller about anything, as you won't know if the caller is looking at or doing anything with the buffer.

Trigger created with warning

As sysdate cannot be used in check constraint, tried to a trigger to fulfill the purpose.
But its showing " Warning: Trigger created with compilation errors."
I am posting my syntax below. I am using Oracle 10g database.
create table birth
(name varchar2(30), DOB date);
insert into birth values ('Ravi', sysdate+1 );
1 row inserted.
Now technically this is wrong. To prevent this I an creating a trigger
create or replace trigger birth_trigger
before insert or update of dob on birth
for each row
begin
if (dob >=sysdate)
then
raise_application_error (-20501, 'DOB cannot be greater than sysdate');
end if;
end;
Now here I am getting the "Warning: Trigger created with compilation errors."
Not been able to resolve the problem.
Please help.
Your problem stems from the fact that you are attempting to reference the "dob" column incorrectly. If you execute, "show errors" , you should see the message, "[Error] PLS-00201 (7: 8): PLS-00201: identifier 'DOB' must be declared". You need to preface the column with the :new prefix (default) to access the record's values.
create or replace trigger birth_trigger
before insert or update of dob on birth
for each row
begin
if ( :new.dob >=sysdate)
then
raise_application_error (-20501, 'DOB cannot be greater than sysdate');
end if;
end;
I would recommend using an IDE to do PL/SQL development, such as Oracle free SQLDeveloper. Also, read the Oracle Documenation on triggers.
use the following code
create or replace trigger birth_trigger
before insert or update of dob on birth
referencing old as old new as new
for each row
begin
if (
trunc ( to_date(:new.dob, 'DD/MM/YYYY' )) >= trunc (to_date(sysdate,'DD/MM/YYYY' ))
)
then
raise_application_error (-20501, 'DOB cannot be greater than sysdate');
end if;
end;
in code i have used new and old keywords for accessing the new values inserted in the birth table.

Error message bad bind variable in trigger

I am trying to write a trigger to validate if the customer/address exist before inserting but i encounter some error. Able to advise on this?
Below is the table:
Customer (CustID,CustName,DOB)
CREATE OR REPLACE TRIGGER CREATEACCOUNT
BEFORE INSERT ON ACCOUNT
FOR EACH ROW
DECLARE
newCustID varchar(10);
newPostCode int;
newStreet char;
newAccType varchar(15);
newAccStatus char(9);
newAccBalance int;
varRowCount int;
BEGIN
newCustID := :new.CustID;
SELECT COUNT(*)
INTO varRowCount
FROM Customer
WHERE CustID = newCustID;
IF (varRowCount > 0) THEN
RETURN;
END IF;
IF (varRowCount = 0) THEN
BEGIN
INSERT INTO CUSTOMER VALUES (newCustID,:new.CustName,:new.DOB);
END;
END IF;
END;
Below is the error message:
Error(27,46): PLS-00049: bad bind variable 'NEW.CUSTNAME'
Error(27,60): PLS-00049: bad bind variable 'NEW.DOB'
The trigger is defined on the ACCOUNT table. You've posted the definition of the CUSTOMER table. Unless the ACCOUNT table has columns CustName and DOB, which seems highly unlikely, you can't refer to :new.CustName or :new.DOB-- the :new record is for the row that is currently being inserted into the ACCOUNT table.
Where do you intend to get the CustName and DOB to insert into the Customer table?
Taking a step back, why is a trigger on the Account table trying to insert a row into the Customer table in the first place. That seems like an exceptionally poor design. The CustID in the Account table would presumably be a foreign key that references the Customer table. That would mean, though, that you could only insert the parent row in a trigger if you declare the constraints as deferable and defer them at the beginning of every transaction. The trigger would also generally have no way of determining the information for the Customer columns that you want to populate which is the source of the error you're getting.

Oracle After Update Trigger error

I want to keep track of changes to one table in another table. What I need is an after update trigger which writes the name of changed column (if multiple columns are changed then there will be multiple inserts to the CHANGES table),the column's old and new values. How do I do that. I tried this but got an error after updating the table.So I'm giving you just the body.
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
the error code is :ORA-04098: trigger 'SYS.TR__TRACK_CHANGES' is invalid and failed re-validation
CREATE OR REPLACE TRIGGER STAJCHANGER.TR_TRACK_CHANGES
AFTER UPDATE
OF STAJYEAR
,STAJMONTH
,STAJDAY
ON STAJCHANGER.STAJ
REFERENCING NEW AS New OLD AS Old
FOR EACH ROW
DECLARE
OLDVALUE NUMBER;
NEWVALUE NUMBER;
COLUMNID NUMBER;
BEGIN
IF :NEW.STAJYEAR!=:OLD.STAJYEAR THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJYEAR',:OLD.STAJYEAR,:NEW.STAJYEAR);
END IF;
IF :NEW.STAJMONTH!=:OLD.STAJMONTH THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJMONTH',:OLD.STAJMONTH,:NEW.STAJMONTH);
END IF;
IF :NEW.STAJDAY!=:OLD.STAJDAY THEN
INSERT INTO X_STAJ (USERID,EDITDATE,CHANGEDCOLUMN,OLDVALUE,NEWVALUE)
VALUES (:NEW.USERID,SYSDATE,'STAJDAY',:OLD.STAJDAY,:NEW.STAJDAY);
END IF;
END TR_TRACK_CHANGES;
/
The error appears to indicates that the trigger owner is SYS, but the creation statement you show explicitly gives the owner as STAJCHANGER.
This makes me wonder, did you accidentally create an (invalid) version of the trigger in SYS at some point, and forget to drop it?
This SQL Plus command will show the error:
SHOW ERROR TRIGGER STAJCHANGER.TR_TRACK_CHANGES

Resources