0

好吧,我正在尝试在触发器中调用存储过程。

我得到的错误

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

这是我的触发器代码:

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;

这是我的存储过程:

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;

这就是我尝试查看触发器是否正常工作的方式:

UPDATE employees SET salary = 100000 WHERE employee_id = 100;

不知何故DBMS_OUTPUT.PUT_LINE,我的触发器代码正在工作。但是存储过程会导致错误。

4

2 回答 2

2

您不能在触发触发器的表上执行SELECT任何其他操作。您DML(INSERT,UPDATE,DELETE)必须使用复合触发器来摆脱变异表错误。

触发器调用的过程正在对触发触发器的雇员表执行 SELECT 操作,并且该操作被 oracle 禁止。

可以在下面找到相同问题的工作示例,请参阅 UPDATE 部分触发选择子记录,乘以它们的值并更新父记录

于 2021-01-06T19:10:12.287 回答
2

首先,你为什么在你的程序中两次查询同一张表?这是对资源的巨大浪费……宁可跑

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

其次,你不能查询你正在更新的同一张表!这是一个巨大的数据(不)一致性问题。你为什么不在一个包/过程中运行它呢?这将使您更好地控制流程

就像是:

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;

当然,这只是一个示例代码,可以为您提供有关如何执行此操作的提示。您将所有逻辑放在一个地方,而不是在两个差异对象中(非常难以调试!!)...在您的生产代码中你必须对输入进行清理,可能会做更多检查,当然正确的索引是必须的 - 但这几乎总结了我会做的事情:)

于 2021-01-06T19:16:26.423 回答