代码之家  ›  专栏  ›  技术社区  ›  PKey

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

  •  0
  • PKey  · 技术社区  · 8 年前

    我有以下情况: 桌子( MyTable )应该由批处理过程(调用 myplsql() 程序)。

    myplsql 任何人都不能碰死刑 我的表 -所以 我的表 被锁定在独占模式 我的生活 是的。

    现在 我的表 有很多 on insert 我是说, on update 我是说, on delete 已定义触发器,但在执行批处理时不需要这些触发器,而且它们会极大地减慢批处理速度。

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

    但如何避免有人触摸 我的表 刚刚 alter table ... disable trigger 在执行之前 我的生活 设法锁上桌子, 既然如此 alter table 执行隐式提交-因此在此之前获得的任何锁都将丢失?

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

    简而言之,我需要在一次拍摄中执行以下操作:

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

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

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

    谢谢。

    1 回复  |  直到 8 年前
        1
  •  2
  •   Alex Poole    8 年前

    一个稍微不同的方法是保持触发器的启用,但是通过添加 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);
    

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