【问题标题】:Oracle PL/SQL Trigger: Reset column value automatically after an update statementOracle PL/SQL 触发器:更新语句后自动重置列值
【发布时间】:2017-10-06 12:58:46
【问题描述】:

出于测试目的,我希望自动化以下场景:

  1. 将我的表的 Status 列设置为 closed 值(使用更新语句)

  2. 提交更新,以便其他用户可以看到 Status

  3. 的新值
  4. 等待 1 分钟

  5. 将我的表的Status列重置为其原始值init(使用触发器)

  6. 提交更新,以便其他用户可以看到 Status

  7. 的原始值

我试过使用这个触发器,但它不起作用,我得到了

ORA-04091: 表名发生变化,触发器/函数可能看不到它

CREATE OR REPLACE TRIGGER RESET_COLUMN
AFTER UPDATE OF STATUS ON MY_TABLE
FOR EACH ROW
WHEN (NEW.STATUS != 'INIT')
BEGIN
    DBMS_LOCK.SLEEP(60);  
    UPDATE MY_TABLE SET STATUS = 'INIT';
    COMMIT;
END;
/

既然不允许在触发器内提交,有没有其他方法可以解决这个问题?使用基于事件的作业?

【问题讨论】:

  • :new.status := 'INIT'; 并从触发器中删除 commit。您不能在触发器内进行任何事务处理
  • 但是我需要提交这些事务,以便其他用户可以使用它们。请查看已编辑的问题

标签: sql oracle plsql triggers oracle12c


【解决方案1】:

要将其作为作业运行,您需要一个可以调用的过程。我假设您想为特定记录而不是整个表执行此操作,因此 ID 参数。

create or replace procedure reset_my_table_status
    ( p_id in number )
is
begin 
    update my_table
    set status = 'INIT'
    where id = p_id;
    commit;
end;
/

然后,从您的触发器提交一个作业以在 60 秒后调用该过程:

CREATE OR REPLACE TRIGGER RESET_COLUMN
AFTER UPDATE OF STATUS ON MY_TABLE
FOR EACH ROW
WHEN (NEW.STATUS != 'INIT')
DECLARE
    jn number;
    pragma autonomous_transaction;
BEGIN
    dbms_job.submit(jn 
                   , what=>'reset_my_table_status('||:new.id||');'
                   , next_date => sysdate + 60/86400
      );
    commit;
END;
/

设置next_date 参数意味着作业将在 60 秒后触发,因此无需调用sleep()。请记住,要运行作业,您需要 JOB_QUEUE_PROCESSES init 参数的值 > 0。我们必须提交作业;所以我们需要有一个自治事务,因为通常我们不能从触发器发出提交。

或者,您可以只构建一个过程(甚至是一个匿名块)。

create or replace procedure my_table_status_test
    ( p_id in number )
is
begin 
    update my_table
    set status = 'MEH'
    where id = p_id;
    commit;

    DBMS_LOCK.SLEEP(60);  

    update my_table
    set status = 'INIT'
    where id = p_id;
    commit;
end;
/

然后只需为您要测试的任何 ID 运行该过程。

【讨论】:

  • 非常感谢。它按预期工作,但我需要在what=>'reset_my_table_status('||:new.id||');' 中添加一个分号,否则我会得到 Oracle - PLS-00103。请看stackoverflow.com/questions/13023625/…
  • 哎呀。对此感到抱歉。
【解决方案2】:

这似乎是个坏主意。 sleep() 在触发器内?这只会复合锁并占用资源。

相反,您可以使用视图或虚拟列。将closeDate 存储为表中的一列(如果您愿意,可以使用触发器进行设置)。

alter table my_table
    add new_status as (case when closeDate > sysdate - 1 / (24*60) then 'closed' else status end);

【讨论】:

  • 谢谢,但在我的情况下修改表结构不是一个选项。
  • @nogc 。 . .视图不是对表结构的修改(虚拟列是,但不是视图)。
猜你喜欢
  • 2012-04-20
  • 2021-02-25
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
  • 2011-10-07
  • 1970-01-01
  • 2010-10-21
相关资源
最近更新 更多