Here’s the problem:
Create a trigger that prevents any change to the taking relation that would drop the overall average grade in any particular class below 2.5. Note: This trigger is not intended to address the average GPA of any given student, but rather it should address the average grade for all grades assigned in a particular class.
Here’s the schema:
Student-schema =(studentnum, name, standing, gpa, major) Class-schema = (schedulenum, semester, department, classnum, days, time, place, enrollment) Instructor-schema = (name, department, office) Teaches-schema = (name, schedulenum, semester) Taking-schema = (studentnum, schedulenum, semester, grade)
I’m having a terrible time with these triggers, but here’s my attempt to make this work:
CREATE OR REPLACE TRIGGER stopChange AFTER UPDATE OR INSERT OR DELETE ON taking REFERENCING OLD AS old NEW AS new FOR EACH ROW DECLARE grd_avg taking.grade%TYPE; BEGIN SELECT AVG(grade) INTO grd_avg FROM taking WHERE studentnum = :new.studentnum AND schedulenum = :new.schedulenum AND semester = :new.semester; IF grd_avg < 2.5 THEN UPDATE taking SET grade = :old.grade WHERE studentnum = :old.studentnum AND schedulenum = :old.schedulenum AND semester = :old.semester; END IF; END; /
I’m obviously doing something wrong because when I then go to update or delete a tuple, I get the error:
ERROR at line 1: ORA-04091: table TAKING is mutating, trigger/function may not see it ORA-06512: at "STOPCHANGE", line 6 ORA-04088: error during execution of trigger 'STOPCHANGE'
Any advice? I’m using Oracle.
Advertisement
Answer
I think you can fix this by rewriting this as a before trigger, rather than an after trigger. However, this might be a little complicated for inserts and deletes. The idea is:
CREATE OR REPLACE TRIGGER stopChange BEFORE UPDATE OR INSERT OR DELETE ON taking REFERENCING OLD AS old NEW AS new FOR EACH ROW DECLARE grd_avg taking.grade%TYPE; BEGIN SELECT (SUM(grade) - oldgrade + new.grade) / count(*) INTO grd_avg FROM taking WHERE studentnum = :new.studentnum AND schedulenum = :new.schedulenum AND semester = :new.semester; IF grd_avg < 2.5 THEN new.grade = old.grade END IF; END;