在更新触发器之前或之后更改同一表中的值(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 PL/SQL 10G)?