Oracle – 触发器无效并且重新验证失败

huangapple go评论97阅读模式
英文:

Oracle - Trigger is invalid and failed to re-validation

问题

我遇到了一个问题,关于更新表时触发器的错误提示是“触发器无效,重新验证失败”。以下是我的触发器代码:

create or replace trigger t_backup_sal before update on employees
for each row 
begin
    merge into sal_backup sb using employees e
    on (sb.emp_id = e.emp_id) 
    when matched then 
      update set sb.emp_id = e.emp_id,
                 sb.salary = e.salary,
                 sb.time_change = sysdate
    when not matched then 
      insert (sb.emp_id, sb.salary, sb.time_change)
      values (e.emp_id, e.salary, sysdate)
end;

如何解决这个错误?

英文:

I get error "Trigger is invalid and failed to re-validation" about my trigger when update table. Here is my trigger:

create or replace trigger t_backup_sal before update on employees
for each row 
begin
    merge into sal_backup sb using employees e
    on (sb.emp_id = e.emp_id) 
    when matched then 
      update set sb.emp_id = e.emp_id,
                 sb.salary = e.salary,
                 sb.time_change = sysdate
    when not matched then 
      insert (sb.emp_id, sb.salary, sb.time_change)
      values (e.emp_id, e.salary, sysdate)
end;

How to solve this error?

答案1

得分: 1

你得到的错误意味着你执行了一个操作(更新了employees表),这导致触发器触发。然而,由于触发器无效,Oracle无法执行其代码,并向你报告了这个问题。

查询user_errors以获取更多信息。


示例表格:

SQL> create table employees
  2  (emp_id number, salary number);

表已创建。

SQL> create table sal_backup
  2  (emp_id number, salary number, time_change date);

表已创建。

触发器:

SQL> create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)
  6      when matched then
  7        update set sb.emp_id = e.emp_id,
  8                   sb.salary = e.salary,
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate)
 13  end;
 14  /

警告:触发器创建时存在编译错误。

出了什么问题?

SQL> select line, position, text
  2  from user_Errors
  3  where name = 'T_BACKUP_SAL';

      LINE   POSITION TEXT
---------- ---------- ----------------------------------------------------------------------
        10         44 PL/SQL: ORA-00933: SQL command not properly ended
         2          5 PL/SQL: SQL Statement ignored
        11          4 PLS-00103: Encountered the symbol "end-of-file" when expecting one of
                      the following:

                         ( begin case declare end exception exit for goto if loop mod
                         null pragma raise return select update while with
                         <an identifier> <a double-quoted delimited-identifier>
                         <a bind variable> << continue close current delete fetch lock
                         insert open rollback savepoint set sql execute commit forall
                         merge pipe purge


SQL&gt;

基本上,它表示你缺少一个分号来终止merge语句:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)
  6      when matched then
  7        update set sb.emp_id = e.emp_id,
  8                   sb.salary = e.salary,
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate);    --&gt; 这里加上分号
 13  end;
 14  /

触发器已创建。

SQL&gt;

那么,现在它工作了吗?

SQL&gt; insert into employees (emp_id, salary) values (1, 100);

已创建 1 行。

SQL&gt; update employees set salary = 200;
update employees set salary = 200
       *
错误行 1:
ORA-38104: 在 ON 子句中引用的列不能被更新: "SB"."EMP_ID"
ORA-06512: 在 "SCOTT.T_BACKUP_SAL" 中
ORA-04088: 触发器 'SCOTT.T_BACKUP_SAL' 的执行中发生错误


SQL&gt;

好吧,又是另一个错误。

回到起点:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)              -- 这里使用了 sb.emp_id
  6      when matched then
  7        update set -- sb.emp_id = e.emp_id,  -- 你不能更新该列,而且这样做没有意义
  8                   sb.salary = e.salary,     -- 你可能想备份先前的值
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate);
 13  end;
 14  /

触发器已创建。

SQL&gt;

重复:

SQL&gt; update employees set salary = 200;
update employees set salary = 200
       *
错误行 1:
ORA-04091: 表 SCOTT.EMPLOYEES 正在变异,触发器/函数可能无法看到它
ORA-06512: 在 "SCOTT.T_BACKUP_SAL" 中
ORA-04088: 触发器 'SCOTT.T_BACKUP_SAL' 的执行中发生错误


SQL&gt;

又是一个错误!这是什么意思?这意味着你不能从正在被修改的表中进行选择。使用伪记录代替(使用:new.:old.来标识ID(始终相同 - 希望你不要修改员工的ID!?)和薪水(你可能想备份先前的值))。

你知道该怎么做:回到起点:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb
  5        using (select :new.emp_id as emp_id,
  6                      :new.salary as salary
  7               from dual) e
  8      on (sb.emp_id = e.emp_id)
  9      when matched then
 10        update set sb.salary = e.salary,
 11                   sb.time_change = sysdate
 12      when not matched then
 13        insert (sb.emp_id, sb.salary, sb.time_change)
 14        values (e.emp_id, e.salary, sysdate);
 15  end;
 16  /

触发器已创建。

现在它工作了吗?是的!

SQL&gt; update employees set salary = 200;

已更新 1 行。

结果:

SQL&gt; select * from sal_backup;

    EMP_ID     SALARY TIME_CHA
---------- ---------- --------
         1        100 09.08.23

SQL&gt;
英文:

Error you got means that you performed action (updated employees table) which caused trigger to fire. However, as trigger is invalid, Oracle can't execute its code and informed you about it.

Query user_errors for more info.


Sample tables:

SQL&gt; create table employees
  2  (emp_id number, salary number);

Table created.

SQL&gt; create table sal_backup
  2  (emp_id number, salary number, time_change date);

Table created.

Trigger:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)
  6      when matched then
  7        update set sb.emp_id = e.emp_id,
  8                   sb.salary = e.salary,
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate)
 13  end;
 14  /

Warning: Trigger created with compilation errors.

What's wrong?

SQL&gt; select line, position, text
  2  from user_Errors
  3  where name = &#39;T_BACKUP_SAL&#39;;

      LINE   POSITION TEXT
---------- ---------- ----------------------------------------------------------------------
        10         44 PL/SQL: ORA-00933: SQL command not properly ended
         2          5 PL/SQL: SQL Statement ignored
        11          4 PLS-00103: Encountered the symbol &quot;end-of-file&quot; when expecting one of
                      the following:

                         ( begin case declare end exception exit for goto if loop mod
                         null pragma raise return select update while with
                         &lt;an identifier&gt; &lt;a double-quoted delimited-identifier&gt;
                         &lt;a bind variable&gt; &lt;&lt; continue close current delete fetch lock
                         insert open rollback savepoint set sql execute commit forall
                         merge pipe purge


SQL&gt;

Basically, it says that you're missing a semi-colon which terminates the merge statement:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)
  6      when matched then
  7        update set sb.emp_id = e.emp_id,
  8                   sb.salary = e.salary,
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate);    --&gt; here
 13  end;
 14  /

Trigger created.

SQL&gt;

So, does it work now?

SQL&gt; insert into employees (emp_id, salary) values (1, 100);

1 row created.

SQL&gt; update employees set salary = 200;
update employees set salary = 200
       *
ERROR at line 1:
ORA-38104: Columns referenced in the ON Clause cannot be updated: &quot;SB&quot;.&quot;EMP_ID&quot;
ORA-06512: at &quot;SCOTT.T_BACKUP_SAL&quot;, line 2
ORA-04088: error during execution of trigger &#39;SCOTT.T_BACKUP_SAL&#39;


SQL&gt;

Well, that's another error.

Back to square 1:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb using employees e
  5      on (sb.emp_id = e.emp_id)              -- sb.emp_id is used here
  6      when matched then
  7        update set -- sb.emp_id = e.emp_id,  -- you can&#39;t update that column, and it
  8                   sb.salary = e.salary,     -- doesn&#39;t make sense to do so
  9                   sb.time_change = sysdate
 10      when not matched then
 11        insert (sb.emp_id, sb.salary, sb.time_change)
 12        values (e.emp_id, e.salary, sysdate);
 13  end;
 14  /

Trigger created.

SQL&gt;

Repeat:

SQL&gt; update employees set salary = 200;
update employees set salary = 200
       *
ERROR at line 1:
ORA-04091: table SCOTT.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at &quot;SCOTT.T_BACKUP_SAL&quot;, line 2
ORA-04088: error during execution of trigger &#39;SCOTT.T_BACKUP_SAL&#39;


SQL&gt;

Another error! What does that mean? It means that you can't select from a table which is just being modified. Use pseudorecord, instead (identified by :new. and :old. for ID (which is always the same - I hope you aren't modifying employee's ID!?) and salary (as you probably want to backup previous value).

You know the drill: back to square 1:

SQL&gt; create or replace trigger t_backup_sal before update on employees
  2  for each row
  3  begin
  4      merge into sal_backup sb
  5        using (select :new.emp_id as emp_id,
  6                      :new.salary as salary
  7               from dual) e
  8      on (sb.emp_id = e.emp_id)
  9      when matched then
 10        update set sb.salary = e.salary,
 11                   sb.time_change = sysdate
 12      when not matched then
 13        insert (sb.emp_id, sb.salary, sb.time_change)
 14        values (e.emp_id, e.salary, sysdate);
 15  end;
 16  /

Trigger created.

Does it work now? Yes!

SQL&gt; update employees set salary = 200;

1 row updated.

Result:

SQL&gt; select * from sal_backup;

    EMP_ID     SALARY TIME_CHA
---------- ---------- --------
         1        100 09.08.23

SQL&gt;

huangapple
  • 本文由 发表于 2023年8月9日 13:00:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/76864711.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定