Search code examples
sqloracle-databaseplsqltriggersora-04091

Trigger Compiles Fine But Encounters ORA-04091 on UPDATE


1.) I coded a trigger for a table called grades as follows:

CREATE OR REPLACE TRIGGER grades_before_update
BEFORE UPDATE OF grade_percent
ON grades
FOR EACH ROW
DECLARE
     grade_percent_var  NUMBER;
BEGIN
     SELECT  grade_percent
     INTO    grade_percent_var
     FROM    grades
     WHERE   student_id = :new.student_id;    

     IF (grade_percent_var > 100 OR grade_percent_var < 0) THEN
          RAISE_APPLICATION_ERROR(-20001, 'grade percent must be between 0 and 100');
     END IF;
END;
/

And it compiles fine. An example of my 2-columned grades table is as follows:

student_id  grade_percent 
1           70
2           85 
3           90
4           70

The problem comes in when I'm trying to test this trigger as follows:

UPDATE grades
SET grade_percent = 80
WHERE student_id = 1;

It gives me an

Error ORA-04091: table GRADES is mutating, trigger/function may not see it. Rewrite the trigger (or function) so it does not read that table.

2.) Also, I plan to include another IF statement in my trigger to check if the new grade_percent inputed is between 0 and 1. If yes, then it should convert that decimal value to whole percentage values for example:

UPDATE grades
SET grade_percent = 0.9
WHERE student_id = 2;

Here's what I have so far and I'm not sure how to write the second line of the code:

 IF (grade_percent_var > 0 OR grade_percent_var < 1) THEN
      INSERT(grade_percent_var * 100);  

The second line should turn any values inserted from 0-1 into whole percents, for example: 0.7 turns into 70 percent and insert it into that column for that row. However, it's a syntax error and I'm not sure how to proceed from here.

Any help is greatly appreciated. Thank you.


Solution

  • First, you don't need a trigger for this:

    alter table grades add constraint chk_grades_grade_percent
        check (grade_percent between 0 and 100);
    

    Second, you can just use the new value for the trigger -- if you have to use a trigger:

    CREATE OR REPLACE TRIGGER grades_before_update
    BEFORE UPDATE OF grade_percent
    ON grades
    FOR EACH ROW
    BEGIN
         IF (:new.grade_percent > 100 OR :new.grade_percent < 0) THEN
              RAISE_APPLICATION_ERROR(-20001, 'grade percent must be between 0 and 100');
         END IF;
    END;
    /