首页 > 解决方案 > 禁用触发器并重新启用触发器,但同时避免表更改

问题描述

我有以下情况: 表 ( MyTable) 应该由批处理(对过程的调用)处理(更新/插入/删除等myplsql())。

myplsql执行期间,任何人都不应触摸MyTable-soMyTable被 锁定为独占模式myplsql

现在定义MyTable了许多on insert, on update,on delete触发器,但在执行批处理时不需要这些触发器 - 而且它们会极大地减慢批处理过程。

所以解决方案是在调用之前禁用触发器myplsql()

但是,如何避免有人触摸MyTable刚刚alter table ... disable trigger执行的操作,并且之前 myplsql设法锁定表,因为它 alter table执行隐式提交 - 所以在此之前获得的任何锁都会丢失?

部分问题是我无法控制其他代码或可能尝试触摸表格的其他用户。

简而言之,我需要一次性执行以下操作:

Lock MyTable 
Disable Triggers (somehow without loosing the lock)
Process MyTable
Enable Triggers
Unlock MyTable  

一种想法是从表中删除授权 - 并使其对其他用户无法使用。

但事实证明 - 这不是一个选项,因为其他进程/用户以表所有者用户身份登录执行他们的操作。

谢谢。

标签: oracleplsqltriggerslocking

解决方案


一个稍微不同的方法是保持触发器启用但减少(如果不是完全消除)它们的影响,通过添加一个when类似的子句:

create or replace trigger ...
...
for each row
when (sys_context('userenv', 'client_info') is null
   or sys_context('userenv', 'client_info') != 'BATCH')
declare
...
begin
...
end;
/

然后在您的程序中在开始时添加一个调用作为“禁用触发器”步骤:

dbms_application_info.set_client_info('BATCH');

并在最后再次清除它,以防会话保持活动状态并重用(因此您可能也希望在异常处理程序中执行此操作):

dbms_application_info.set_client_info(null);

您还可以使用模块、操作或组合。虽然该设置到位,但触发器仍将被评估但不会触发,因此内部发生的任何事情都将被跳过 - 正如文档所说,触发器主体不会运行。

这并不是万无一失的,因为没有什么能真正阻止其他用户/应用程序进行相同的调用,但是如果您选择更具描述性的字符串和/或设置组合,则必须经过深思熟虑 - 我认为您主要是担心事故不坏演员。


使用毫无意义的触发器进行快速速度测试,只会让事情变慢一点。

create table t42 (id number);

-- no trigger
insert into t42 (id) select level from dual connect by level <= 10000;

10,000 rows inserted.

Elapsed: 00:00:00.050

create or replace trigger tr42 before insert on t42 for each row
declare
  dt date;
begin
  select sysdate into dt from dual;
end;
/

-- plain trigger
insert into t42 (id) select level from dual connect by level <= 10000;

10,000 rows inserted.

Elapsed: 00:00:00.466

create or replace trigger tr42 before insert on t42 for each row
when (sys_context('userenv', 'client_info') is null
   or sys_context('userenv', 'client_info') != 'BATCH')
declare
  dt date;
begin
  select sysdate into dt from dual;
end;
/

-- userenv trigger, not set
insert into t42 (id) select level from dual connect by level <= 10000;

10,000 rows inserted.

Elapsed: 00:00:00.460

- userenv trigger, set to BATCH

exec dbms_application_info.set_client_info('BATCH');

insert into t42 (id) select level from dual connect by level <= 10000;

10,000 rows inserted.

Elapsed: 00:00:00.040

exec dbms_application_info.set_client_info(null);

进行远程调用有一些变化,但我跑了几次,很明显,使用普通触发器运行与使用未设置 BATCH 的受限触发器运行非常相似,并且两者都比不使用触发器或使用触发器运行要慢得多设置了 BATCH 的约束触发器。在我的测试中有一个数量级的差异。


推荐阅读