我有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。是否可以使用触发器更改此表中的值?
答案 0 :(得分:3)
这是变异表问题。它发生的原因是因为您编写了一个UPDATE触发器,它尝试在同一个表上执行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更新当前status
记录的valid
,然后将更新应用于目标行。