在更新触发器之前或之后更改同一表中的值(oracle)

Posted

技术标签:

【中文标题】在更新触发器之前或之后更改同一表中的值(oracle)【英文标题】:Change values in the same table before or after update trigger (oracle) 【发布时间】:2018-02-19 07:24:43 【问题描述】:

我有 3 个字段的表格:

--------------------------------------------------
| ID | FILE        |          STATUS             |
--------------------------------------------------
| 1  | my.exe      |           valid             |
--------------------------------------------------
| 2  | my.exe      |           invalid           |
--------------------------------------------------
| 3  | my.exe      |           invalid           |
--------------------------------------------------
| 4  | my.exe      |           invalid           |
--------------------------------------------------

这是一个文件的某些版本。当我将其中任何一个的状态更新为“有效”时,我需要所有具有此名称的文件将其状态更改为“无效”, 除了一个更新:

--------------------------------------------------
| ID | FILE        |          STATUS             |
--------------------------------------------------
| 1  | my.exe      |           invalid           |
--------------------------------------------------
| 2  | my.exe      |           invalid           |
--------------------------------------------------
| 3  | my.exe      |            valid            |
--------------------------------------------------
| 4  | my.exe      |           invalid           |
--------------------------------------------------

我认为可以在更新触发器之前完成:

create or replace trigger ChangeValid
  before update
  on mytable 
  for each row
declare

begin
   update mytable t set t.status = 'ivalid' where t.status = 'valid' and t.file = :new.file;

end ChangeValid;

但我好心得到 ORA-04091。有没有办法用触发器改变这个表中的值?

【问题讨论】:

您可能会因为这个new.file;而遇到问题,请检查该列的数据类型,点击此链接以获取更多信息asktom.oracle.com/pls/asktom/… 删除查询中的declare 感谢您的回答。原来改变status值的时候没有办法自动更新表中的数据? 【参考方案1】:

这是变异表问题。它发生的原因是因为您编写了一个 UPDATE 触发器,它试图在同一个表上执行 UPDATE 语句。当您更新其他行时,您认为会发生什么?触发器尝试触发,这意味着它会递归地执行更新语句,依此类推。 Oracle 通过禁止行级触发器作用于自己的表并抛出ORA-04091 来缩短废话。

您可以使用复合触发器解决此问题:

create or replace trigger ChangeValid
for update on mytable compound trigger 

  type rec_nt is table of mytable%rowtype;
  recs rec_nt;

  before statement is 
  begin
    recs := rec_nt();
  end before statement;

  before each row is 
  begin
    null;
  end before each row;

  after each row is 
  begin
    recs.extend();
    recs(recs.count()).id := :new.id;
    recs(recs.count()).file_name := :new.file_name;
    recs(recs.count()).status := :new.status;
  end after each row;

  after statement is 
  begin

    for idx in 1 .. recs.count() loop
      if recs(idx).status = 'valid' then
          update mytable t
          set t.status = 'invalid'
          where t.file_name = recs(idx).file_name
          and t.status = 'valid'
          and t.id != recs(idx).id;
      end if;
    end loop;  

  end after statement;

end;
/

请注意,此触发器将运行两次:一次用于您更新的行,一次用于触发器运行时更​​新的所有行。这就是为什么我们需要在after statement 部分中围绕UPDATE 的IF 语句。您还需要注意 UPDATE 不会触发递归。

因此,如果您有很多 my.exe 的记录,这可能是实现此类逻辑的一种昂贵的方式。更好的方法是使用 PL/SQL API 更新当前 valid 记录的 status,然后将更新应用于目标行。

【讨论】:

以上是关于在更新触发器之前或之后更改同一表中的值(oracle)的主要内容,如果未能解决你的问题,请参考以下文章

Oracle:使用来自同一表的聚合值更新表中的值

如何使用触发器在同一张表中插入新行(Oracle PL/SQL 10G)?

同一张表中的行之间的差异(Oracle SQL)

Oracle PL / SQL触发器,在UPDATE之前/之后仅用于识别表中已修改的列

可以为当前会话禁用 Oracle 触发器吗?

Oracle更新触发器在同一个表上