Oracle PL/SQL 触发器:更新语句后自动重置列值

Oracle PL/SQL Trigger: Reset column value automatically after an update statement

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

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

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

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

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

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

  7. 的原始值

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

ORA-04091: table name is mutating, trigger/function may not see it

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

既然在trigger里面是不允许commit的,请问还有什么办法可以解决这个问题吗?使用基于事件的作业?

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

相反,您可以使用视图或虚拟列。将 closeDate 作为列存储在 table 中(您可以根据需要使用触发器来设置它)。

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

要运行将此作为一项工作,您需要一个可以调用的过程。我假设您想为特定记录而不是整个 table 执行此操作,因此是 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参数意味着作业将在六十秒内触发,因此不需要sleep()调用。请记住,对于 运行 的作业,您需要 JOB_QUEUE_PROCESSES 初始参数的值 > 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 的过程。