oracle - 禁用触发器并重新启用触发器,但同时避免表更改
问题描述
我有以下情况: 表 ( 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
一种想法是从表中删除授权 - 并使其对其他用户无法使用。
但事实证明 - 这不是一个选项,因为其他进程/用户以表所有者用户身份登录执行他们的操作。
谢谢。
解决方案
一个稍微不同的方法是保持触发器启用但减少(如果不是完全消除)它们的影响,通过添加一个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 的约束触发器。在我的测试中有一个数量级的差异。
推荐阅读
- python - 在使用 Google Collab 中的所有可用 RAM 后,您的会话崩溃了
- gis - QGIS DB Manager - ST_Buffer 产生空值
- python - 在混合数据类型数据框中的列子集中删除行
- django - 如何在 django 中按特定的 ForeignKey 排序?
- python - 如何将矩阵(或列表列表)与空格和字母组合成字符串?
- scikit-learn - 置信区间的高斯过程回归估计
- javascript - 为什么javascript onchange事件仅适用于数据表的第一个模式
- html - Material Design Icons 上类似 FontAwesome 的固定宽度
- performance - 在仔细的配置文件中解释荒谬的低测量延迟(超标量效应?)
- django - 无法分配“'2'”:“TakenQuiz.question”必须是“Question”实例