【问题标题】:Call a stored procedure in a trigger PL/SQL在触发器 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 正在工作。但是存储过程会导致错误。

【问题讨论】:

    标签: plsql triggers oracle-sqldeveloper mutating-table


    【解决方案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;
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-13
        • 1970-01-01
        • 1970-01-01
        • 2011-04-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多