禁用触发器并重新启用触发器,但同时避免更改表

时间:2018-07-09 14:30:35

标签: oracle plsql triggers locking

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

在执行myplsql期间,任何人都不能触摸MyTable-因此MyTablemyplsql锁定为独占模式。

现在MyTable定义了多个on inserton updateon 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  

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

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

谢谢。

1 个答案:

答案 0 :(得分:2)

另一种略有不同的方法是,通过添加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;
/

然后在您的过程add a call at the start中作为“禁用触发器”步骤:

dbms_application_info.set_client_info('BATCH');

并在最后将其清除,以防万一会话保持活动状态并可以重用(因此,您可能也想在异常处理程序中执行此操作):

dbms_application_info.set_client_info(null);

您也可以使用模块,动作或组合。设置到位后,触发器仍将被评估但不会触发,因此内部发生的任何事情都将被跳过-触发器主体无法运行,如the docs所述。

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


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

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进行运行慢得多触发器或带有BATCH设置的约束触发器。在我的测试中,存在一个数量级的差异。