Call a stored procedure in a trigger PL/SQL

606 Views Asked by At

Well I'm trying to call a stored procedure in my trigger.

The error I'm getting

"table %s.%s is mutating, trigger/function may not see it"
*Cause:    A trigger (or a user defined plsql function that is referenced in
           this statement) attempted to look at (or modify) a table that was
           in the middle of being modified by the statement which fired it.
*Action:   Rewrite the trigger (or function) so it does not read that table.

This is my code of the trigger:

create or replace TRIGGER check_salary_trg 
AFTER INSERT OR UPDATE ON employees
FOR EACH ROW

BEGIN 
DBMS_OUTPUT.PUT_LINE(:new.job_id ||' '|| :new.salary);
check_salary(:new.job_id, :new.salary);
END;

This is my stored procedure:

PROCEDURE check_salary (job_id employees.job_id%TYPE, salary employees.salary%TYPE)
IS
maxSal NUMBER;
minSal NUMBER;
BEGIN
SELECT MAX(salary) INTO maxSal FROM employees WHERE job_id = job_id;
SELECT MIN(salary) INTO minSal FROM employees WHERE job_id = job_id;
IF maxSal >= salary OR minSal <= salary THEN
RAISE_APPLICATION_ERROR(-20001,'Invalid slary '||salary||'. Salaries for job '||job_id||' must be between '||minSal||' and '||maxSal);
ELSE
DBMS_OUTPUT.PUT_LINE('Test');
END IF;
END;

This is how I try to see that the trigger is working:

UPDATE employees SET salary = 100000 WHERE employee_id = 100;

Somehow the DBMS_OUTPUT.PUT_LINE in my Trigger code is working. But the stored procedure causes the error.

2

There are 2 best solutions below

0
VN'sCorner On

You can't do SELECT or any other DML(INSERT,UPDATE,DELETE) on the table on which trigger is being fired.You have to use compound trigger to get away with mutating table error.

The procedure invoked by trigger is doing a SELECT on the employee table on which trigger is firing and that is forbidden by oracle.

A working example for same issue can be found under, refer UPDATE section Trigger selecting child records, multiplying their values and updating parent record

0
Mr.P On

first of all, why do you query the same table twice in your procedures? that's a huge waste of resources... rather run

SELECT min(salary), max(salary) INTO minSal, maxSal
FROM employees 
WHERE job_id = job_id

Second of all, you cannot query the same table you are update.ing !! That's a huge data (in)consistency issue. Why don't you run this in a package/procedure instead? That will give you way better control over your flow

Something like:

CREATE OR REPLACE PROCEDURE prcd_update_salary(p_emp_id INT, p_salary INT)
IS
 maxSal INT;
 minSal INT;
 job_id INT;
BEGIN


    SELECT job_id, min(salary), max(salary) INTO job_id, minSal, maxSal
    FROM employees 
    WHERE job_id = (SELECT job_id FROM employees WHERE employee_id = p_emp_id);

    IF (p_salary >= minSal AND p_salary <= maxSal) THEN 
          UPDATE employees SET salary = p_salary WHERE employee_id = p_emp_id;
    ELSE 
          dbms_output.put_line('Sorry, this is out of range!')
          dmbs_output.put_line('You can only use from '||minSal||' up to '||maxSal||' for a job id: '|| job_id);
    END IF;
    
END;

This is, of course, only a sample code to give you hints on how to do that.. you have all the logic in one place and not in two diff objects (very hard to debug !!) ... in your production code you have to sanitize the input a maybe do a bit more checks and of course proper indexing is a must - but this pretty much summarizes what I would do :)