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

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

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);

Table created.

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

Table created.

触发器:

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  /

Warning: Trigger created with compilation errors.

出了什么问题?

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>

基本上,它说您缺少一个分号,该分号终止了merge语句:

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  /

Trigger created.

SQL>

那么,现在可以工作了吗?

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

1 row created.

SQL> 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: "SB"."EMP_ID"
ORA-06512: at "SCOTT.T_BACKUP_SAL", line 2
ORA-04088: error during execution of trigger 'SCOTT.T_BACKUP_SAL'


SQL>

好吧,又是一个错误。

回到原点:

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)              -- sb.emp_id is used here
  6      when matched then
  7        update set -- sb.emp_id = e.emp_id,  -- you can't update that column, and it
  8                   sb.salary = e.salary,     -- doesn'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>

重复:

```SQL
SQL> 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 "SCOTT.T_BACKUP_SAL", line 2
ORA-04088: error during execution of trigger 'SCOTT.T_BACKUP_SAL'


SQL>

另一个错误!这是什么意思?这意味着您不能从正在被修改的表中选择。使用伪记录代替(使用:new.:old.来引用ID(始终相同 - 希望您没有修改员工的ID!)和工资(因为您可能想备份先前的值)。

您知道该怎么做:回到原点:

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

SQL>

现在可以工作了吗?是的!

```SQL
SQL> update employees set salary = 200;

1 row updated.

结果:

SQL> select * from sal_backup;

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

SQL>
英文:

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-2.html
匿名

发表评论

匿名网友

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

确定