在触发器 PL/SQL 中调用存储过程
Posted
技术标签:
【中文标题】在触发器 PL/SQL 中调用存储过程【英文标题】:Call a stored procedure in a trigger PL/SQL 【发布时间】:2021-01-06 18:48:11 【问题描述】:我正在尝试在触发器中调用存储过程。
我遇到的错误
"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
正在工作。但是存储过程会导致错误。
【问题讨论】:
【参考方案1】:您不能在触发触发器的表上执行SELECT
或任何其他DML(INSERT,UPDATE,DELETE)
。您必须使用复合触发器来避免变异表错误。
触发器调用的过程正在对触发触发器的员工表执行 SELECT,并且被 oracle 禁止。
可以在下面找到相同问题的工作示例,请参阅更新部分Trigger selecting child records, multiplying their values and updating parent record
【讨论】:
【参考方案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;
当然,这只是一个示例代码,可以为您提供有关如何执行此操作的提示。您将所有逻辑放在一个地方,而不是在两个差异对象中(非常难以调试!!)...在你的生产代码你必须清理输入,可能会做更多检查,当然正确的索引是必须的 - 但这几乎总结了我会做什么:)
【讨论】:
以上是关于在触发器 PL/SQL 中调用存储过程的主要内容,如果未能解决你的问题,请参考以下文章