简介
有时您会想暂时禁用表上的触发器。例如,尽管您可能需要触发器执行日常的 SQL
操作,但可能不希望在运行特定脚本时触发那些触发器。标准做法是删除触发器,当再次需要它时再重新创建,但如果您必须跟踪许多触发器,那就有点为难
了。(现在,我该把那些触发器的源代码保存到哪里呢?)
本文提供了三种解决这个问题的方法:
方法 1:对特定用户禁用触发器 方法 2:用框架来允许禁用触发器 方法 3:使用 SQL 存储过程维护触发器
每种方法都有其优缺点,但我们将这方面的 讨论留到文章末尾。
方法 1:对特定用户禁用触发器
用来执行数据库维护任务的用户标识通常与用于应用程序的用户标识不同,这种方法利用了这一情况。要使这种方法有效,您只需选择在不希望触发触发器时要使用的用户标识。
example1.db2脚本中的 SQL 向您演示了这种方法。
设置
要设置这个示例:
创
建两个表 t1 和 t2 。我们将在 t1 上创建一个样本触发器,它将引起对 t2 进行插入操作。 CREATE TABLE
db2admin.t1 (c1 int)CREATE TABLE db2admin.t2 (c1 int)创建触发器: CREATE
TRIGGER db2admin.trig1AFTER INSERT ON db2admin.T1REFERENCING NEW AS o
FOR EACH ROW MODE DB2SQLWHEN (USER <> 'ADMINISTRATOR')BEGIN
ATOMICINSERT INTO db2admin.t2 values (o.c1);END
这个触发器很简单。每当连接的用户标识(由 USER 专用寄存器返回的)与 ADMINISTRATOR 不匹配时,则将插入到 t1 中的值也插入到 t2 。因此,当不想触发触发器时,以用户 ADMINISTRATOR进行连接以执行您的任务。
测
试示例 在创建了表 t1 、 t2 和触发器 trig1 之后,以不同于 ADMINISTRATOR 的任何用户进行连接并将值插入 t1 。
INSERT INTO db2admin.t1 VALUES (111)验证该值已由触发器复制到了表 t2 中: SELECT * FROM
db2admin.t2C1----------- 111 1 record(s) selected.接下来,以用户 ADMINISTRATOR
连接,并尝试再次插入值: INSERT INTO t1 VALUES (222)验证表 t2 未更改,因为触发器未被激活: SELECT *
FROM db2admin.t2C1----------- 111 1 record(s) selected.
方法 2:用框架来允许禁用触发器
本节描述了一个触发器框架,您可以将它用于任何可能需要暂时禁用的触发器。使用框架要求触发器开发人员进行规划并对这种概念取得一致意见,但这样做的结果可以得到该问题非常清晰的解决方案。
example2.db2脚本中的 SQL 向您演示了这种方法。
下面说明了这种机制的工作原理:
定
义触发器查找表 trigger_state ,它维护一个由触发器的名称和状态(active='Y' 或 'N')所组成的列表
在定义触发器时,向 trigger_state 表添加一次查寻(在该触发器的 WHEN 子句中)以确定该触发器是否应该激活 设置
要设置这个示例:
创
建两个表 t1 和 t2 。我们将在 t1 上创建一个样本触发器,它将引起对 t2 进行插入操作。 CREATE TABLE
db2admin.t1 (c1 int)CREATE TABLE db2admin.t2 (c1 int)创建 trigger_state
表。 CREATE TABLE db2admin.trigger_state (trigschema VARCHAR(128) not
null,trigname VARCHAR(30) not null, active char(1) not null)
乍一看,您很可能想在含有 trigschema 和 trigname 列的 trigger_state 表中放置一个主键。目前,我们先不在表上放置任何约束。
假
定您想要在表 t1 上创建名为 trig1 的触发器。我们要做的第一件事情是向 trigger_state 表注册该触发器: INSERT
INTO db2admin.trigger_state VALUES ('DB2ADMIN','TRIG1','Y')
提示:对所有值都使用 大写,与系统目录表保持一致。
接
下来,为方便起见,我们将创建用户定义的函数(UDF)。当我们创建触发器时,它的用途将变得很明显: CREATE FUNCTION
db2admin.trigger_enabled (v_schema VARCHAR(128), v_name
VARCHAR(30))RETURNS VARCHAR(1)RETURN (SELECT active FROM
db2admin.trigger_state WHERE trigschema=v_schema and trigname=v_name)
重要:如果查寻失败,则这个函数返回空值。因此,确保正确填写 trigger_state 表,并在调用这个函数时传递正确的参数。
如您所见,该函数将模式和触发器的名称作为输入,以在 trigger_state 表中执行查寻,并返回 active 列中的值。
创
建触发器: CREATE TRIGGER db2admin.trig1AFTER INSERT ON
db2admin.T1REFERENCING NEW AS oFOR EACH ROW MODE DB2SQLWHEN
(db2admin.trigger_enabled('DB2ADMIN','TRIG1') = 'Y')BEGIN ATOMICINSERT
INTO db2admin.t2 values (o.c1);END
这个触发器很简单。当启用它时,插入 t1 的值也会插入到 t2 。但是,在激活它之前,它调用 UDF trigger_enabled() 来确定该触发器是否被禁用。用该函数封装这个查询降低了出错的可能性,尤其是在需要创建许多触发器的情况下。
提示:如果您的触发器已经将 WHEN 子句用于其它条件,则只需用 AND 操作符将条件串到一起。
测
试示例 首先,我们测试该触发器是否按预期的方式工作: INSERT INTO db2admin.t1 values (123)DB20000I
The SQL command completed successfully.验证 t2 也包含值 123,因为激活了触发器: SELECT
* FROM db2admin.t2C1----------- 123 1 record(s) selected.现在,我们将禁用该触发器:
UPDATE db2admin.trigger_state SET active='N' WHERE
trigschema='DB2ADMIN' and trigname='TRIG1'然后将另一行插入 t1 : INSERT INTO
db2admin.t1 values (456)现在,让我们通过确定表 t2 未经更改来验证触发器已被禁用。 SELECT * FROM
db2admin.t2C1----------- 123 1 record(s) selected.
重新启用触发器
要重新启用触发器,只要再次设置触发器的状态。
UPDATE db2admin.trigger_state SET active='Y' WHERE trigschema='DB2ADMIN' and trigname='TRIG1'
性能优化
至此,我们还没有在 trigger_state 上创建任何唯一性约束或索引,因为有理由对此进行更彻底的讨论,另外还因为演示该技术时不需要这些约束或索引。
因
为 trigger_state 可能维护数百甚至数千个触发器,所以我们希望使对该表执行查寻的开销最小化。与其在列 trigschema 和
trigname 上创建主键(由它又可以创建唯一性索引),不如将创建这个唯一性索引作为单独的步骤,以便我们在该索引页中包含名为 active
的列。从基表取回多余的字节会占用额外的 I/O,这是对资源的浪费。
以下是该索引的定义,它用关键字 INCLUDE来指定在这个唯一性索引中添加 active 列:
CREATE UNIQUE INDEX db2admin.trigstateIX ON db2admin.trigger_state (trigschema, trigname) INCLUDE (active)
如
果使用 trigger_state
表来维护数千个触发器,则可能希望将这个表放在它自己的表空间中,并给它指定专门的缓冲池。这样,就可以将该查找表始终保存在内存中。尝试确定缓冲池的大
小,以便确保 trigger_state 的所有行都在内存中,但不要使缓冲池太大以至浪费内存(您可以使用命令 LIST TABLESPACES
SHOW DETAIL 的输出来帮助确定缓冲池的大小)。如果触发器数目在几千之内,则这种优化可能是不值得的,因为,假定
trigger_state 的行大小只有 41 字节左右(假定 trigschema 占 20 个字节, trigname 占 20
个字节,状态占 1 个字节),那么每 4 KB 的页能存储 100 个触发器的信息。
如果您有几千个触发器,请记住对 trigger_state 表运行统计。
当然,另一个重要的考虑事项是,只对需要定期禁用的触发器才使用这种技术。
方法 3:使用 SQL 存储过程维护触发器
在
方法 1和 方法
2中,我们描述了禁用触发器的方法,这样您就不必为删除和重建它们所引发出来的问题而担心了。在本节中,我们提供了一种解决方案,它使用 SQL
存储过程来封装和管理触发器的删除和重建。其机制是这样设计的:源代码始终存在于数据库中,因此不必跟踪触发器的源代码。
以下是这种机制的工作原理:
创
建三个存储过程: disable_trigger() — 禁用触发器 enable_trigger() — 启用触发器
show_disabled_triggers() — 显示所有已禁用的触发器 创建名为 trigtool.disabled_triggers
的表,它看上去类似于 syscat.triggers 系统目录表。这个表维护已删除触发器的副本。最初它是空的。 当需要禁用触发器时,调用
disable_trigger() ,它将触发器定义从 syscat.triggers 复制到 disabled_triggers
表,然后删除这个触发器。 要启用触发器,调用 enable_trigger() ,它从 trigtool.disabled_triggers
表重新创建触发器。
限制:不能通过这种方法禁用代码文本超过大约 30KB 的触发器。
用存储过程禁用和启用触发器提供了一种有别于实际删除和重建触发器的抽象级别。我们提供了实现该存储过程的所有源代码。但是,请确保阅读后面有关我们代码的 免责声明。
设置
使用 trigtool 模式创建所有对象,并且脚本 example3.db2提供了所有 DDL。
创
建 32KB 页大小的缓冲区和 32KB 页大小的表空间。 CREATE BUFFERPOOL BP32K SIZE 1000
PAGESIZE 32KCREATE TABLESPACE TS32K PAGESIZE 32KMANAGED BY SYSTEM USING
('c:\ts32k\') BUFFERPOOL BP32K创建 trigtool.disabled_triggers 表: CREATE
TABLE TRIGTOOL.DISABLED_TRIGGERS (TRIGSCHEMA VARCHAR(128) not
null,TRIGNAME VARCHAR(128) not null,TABSCHEMA VARCHAR(128) not
null,TABNAME VARCHAR(128) not null,QUALIFIER VARCHAR(128) not
null,FUNC_PATH VARCHAR(254) not null,TEXT VARCHAR(31500) not null) in
TS32KALTER TABLE TRIGTOOL.DISABLED_TRIGGERS ADD CONSTRAINT
disabledtrig_pk PRIMARY KEY (trigschema, trigname)
以下是这个表的一些重要特性:
该
表看起来几乎(但并非完全)与 syscat.triggers 相同。我们只包括了需要用来重新创建触发器的列。 该表是在表空间 TS32K
中创建的,该表空间是 32KB 页大小在触发器模式与触发器名称上创建了主键约束TEXT 列的类型是 VARCHAR(31500),这与
syscat.triggers 中使用 CLOB 类型的 TEXT 列不同。稍后讨论原因。 创建
trigtool.show_disabled_triggers()
过程,它提供了一个显示当前已禁用触发器的方法。基本上,它将已禁用游标的模式和名称作为一个游标返回给该过程的调用者,应用程序或用户可以从命令行处理
器(CLP)检索它。以下是这个过程的源代码: CREATE PROCEDURE TRIGTOOL.SHOW_DISABLED_TRIGGERS
()LANGUAGE SQLRESULT SETS 1BEGINDECLARE c_triggers CURSOR WITH RETURN
FOR SELECT trigschema, trigname FROM TRIGTOOL.DISABLED_TRIGGERS;OPEN
c_triggers;END既然我们拥有查看已禁用触发器的方法,我们就可以创建一个名为 trigtool.disable_trigger()
的过程,它实际复制和删除触发器。 CREATE PROCEDURE TRIGTOOL.DISABLE_TRIGGER ( IN
v_schema VARCHAR(128), IN v_name VARCHAR(128))SPECIFIC
DISABLE_TRIGGERLANGUAGE SQLBEGINDECLARE SQLCODE INT DEFAULT 0;DECLARE
v_stmt VARCHAR(250);DECLARE EXIT HANDLER FOR NOT FOUND SIGNAL SQLSTATE
'80000' SET MESSAGE_TEXT='Trigger Not Found';DECLARE EXIT HANDLER FOR
SQLWARNING SIGNAL SQLSTATE '80001' SET MESSAGE_TEXT='Unable to disable
trigger';INSERT INTO TRIGTOOL.DISABLED_TRIGGERSSELECTTRIGSCHEMA,
TRIGNAME, TABSCHEMA, TABNAME,QUALIFIER, FUNC_PATH, CAST(TEXT as
VARCHAR(31500))FROM SYSCAT.TRIGGERSWHERE TRIGSCHEMA = v_schema and
TRIGNAME = v_name AND VALID='Y';SET v_stmt = 'DROP TRIGGER ' ||
v_schema || '.' ||v_name;EXECUTE IMMEDIATE v_stmt;END
该过程接收两个参数:要禁用的触发器的模式和名称。
第
一个操作是 INSERT,它从 syscat.triggers 表将信息复制到 trigtool.disabled_triggers 表。注:
syscat.triggers 的 TEXT 列的 CLOB 数据类型被强制转化成了 VARCHAR(31500)
数据类型。复制完成之后,就用动态 SQL
删除触发器。因为没有定义异常处理程序,所以发生的任何错误都将导致回滚,从而使这个操作被拒绝。为了安全,声明一个 SQLWARNING
处理程序。这个处理程序发出 SQLEXCEPTION
信号,该信号将引起回滚。换句话说,只有在整个操作序列都完成,并不出现错误或警告的情况下,触发器才会被删除。
创
建 trigtool.enable_trigger() ,它将从 trigtool.disabed_triggers 表重新创建触发器。
CREATE PROCEDURE TRIGTOOL.ENABLE_TRIGGER ( IN v_schema VARCHAR(128), IN
v_name VARCHAR(128))LANGUAGE SQLBEGINDECLARE SQLCODE INT DEFAULT
0;DECLARE v_qualifier VARCHAR(128);DECLARE v_func_path
VARCHAR(1000);DECLARE v_stmt VARCHAR(32672);DECLARE v_curr_qualifier
VARCHAR(128);DECLARE v_curr_funcpath VARCHAR(1000);DECLARE EXIT HANDLER
FOR SQLWARNING SIGNAL SQLSTATE '80000'SET MESSAGE_TEXT = 'Error. Manual
recreation required';SET v_curr_qualifier = CURRENT SCHEMA;SET
v_curr_funcpath = CURRENT FUNCTION PATH;SELECT qualifier, func_path,
TEXT into v_qualifier, v_func_path, v_stmtFROM
TRIGTOOL.DISABLED_TRIGGERS WHERE trigschema=v_schema and
trigname=v_name;SET v_func_path = 'SET CURRENT FUNCTION PATH = ' ||
v_func_path;EXECUTE IMMEDIATE v_func_path;SET v_qualifier = 'SET
CURRENT SCHEMA = ' || v_qualifier;EXECUTE IMMEDIATE v_qualifier;EXECUTE
IMMEDIATE v_stmt;DELETE FROM TRIGTOOL.DISABLED_TRIGGERS WHERE
trigschema=v_schema and trigname=v_name;SET v_curr_qualifier = 'SET
SCHEMA = ' || v_curr_qualifier;SET v_curr_funcpath = 'SET CURRENT
FUNCTION PATH = ' || v_curr_funcpath;EXECUTE IMMEDIATE
v_curr_qualifier;EXECUTE IMMEDIATE v_curr_funcpath;END
该过程(与前面那个删除触发器的过程类似)接收两个参数:要启用触发器的模式和名称。首先,存储当前会话的当前模式和当前函数路径,以便我们能够在
该过程执行完成之后恢复它。然后,从 trigtool.disabled_triggers 表检索 qualifier, func_path和
text。
qualifier所包含的模式在最初创建触发器时限定未限定的表和视图。类似地, func_path值表示在最初创建触发器时使用的函数路径。函数路径用来解析触发器定义中可能存在的未限定函数。 text列包含用来创建触发器的原始文本。
在
恢复触发器之前,设置函数路径和当前模式值,以便在执行触发器文本时,对所有未限定的对象引用使用适当的限定符和函数路径。然后,使用
text,重新创建触发器,并 从 trigtool.disabled_triggers 删除该触发器的副本。您可以从代码中看出存在 30 KB
触发器文本大小限制的原因。EXECUTE IMMEDIATE 不支持 CLOB 类型作为参数,这就是我们必须将来自
syscat.triggers 的原始文本从 CLOB 强制转化成 VARCHAR 的原因。
最后,将当前模式和当前函数路径恢复成它们原先的值。
就象 trigtool.disable_trigger() 一样,整个操作序列执行期间必须不出现任何错误或警告,否则整套操作都会回滚。
测试示例
在把一切都设置妥当之后,可以测试触发器的禁用和启用了。这个测试还提供了 example4.db2脚本。要设置这个示例:
创
建下列两个表: CREATE TABLE db2admin.t1 (c1 int)CREATE TABLE db2admin.t2 (c1
int)测试该触发器是否按预期的方式工作: INSERT INTO db2admin.t1 values (123)DB20000I The
SQL command completed successfully.验证 t2 是否也具有值 123,因为激活了触发器。 SELECT *
FROM db2admin.t2C1----------- 123 1 record(s) selected.现在,我们将禁用该触发器:
CALL trigtool.disable_trigger('DB2ADMIN','TRIG1')可以通过调用
trigtool.show_disabled_triggers() 来验证触发器已被禁用: CALL
trigtool.show_disabled_triggers()Result set 1--------------TRIGSCHEMA
TRIGNAME--------------------------- --------------------DB2ADMIN TRIG11
record(s) selected.Return Status = 0现在,将另一行插入 t1 : INSERT INTO
db2admin.t1 values (456)SELECT * FROM db2admin.t2C1----------- 123 1
record(s) selected.
正如预料,表 t2 未发生改变,因为触发器已禁用。
重新启用触发器
要重新启用触发器,只要用模式和触发器的名称来调用 enable_trigger() 存储过程,如下所示:
CALL trigtool.enable_trigger('DB2ADMIN','TRIG1')
为
了简化讨论,这个示例测试没有完整演示该存储过程的正确性。但是,在使用与缺省情况不同的模式和函数路径(即,用随机挑选的 CURRENT
SCHEMA和 CURRENT FUNCTION PATH专用寄存器进行限定)创建触发器时,已经通过测试证明它工作正常。在脚本
example5.db2中,提供了一个复杂情况下的测试用例。对于这个复杂案例的分析,我们留给读者作为一个练习。
结束语
本文演示了三种用于禁用和启用触发器的方法:通过用户、通过查找表和通过用存储过程管理触发器的删除和重新创建。每种技术都有其优缺点,而每种环境具有的不同要求,会造成某种方法比另一种优越。
表 1总结了每种方法的优缺点。
随时欢迎您的反馈,可通过 ypaul@ca.ibm.com将反馈发送给 Paul Yip。
表 1. 比较三种禁用和启用触发器的方法
方
法 优点 缺点
对用户禁用不删除触发器无性能开销对于简单需求很理想必须使用特定的用户标识以避免激活触发器触发器框架不删除触发器如果正在开发新触发器,则很理想需要
触发器开发人员对使用框架取得一致少量的性能开销存储过程无性能开销触发器源代码全部存在于数据库中通过存储过程达到了完全的抽象如果目前已经创建了许多
触发器,则该方法很理想包可能需要自动重新联编触发器被实际删除创建 SQL 存储过程需要受支持的 C 编译器30KB 触发器大小的限制
分享到:
相关推荐
在DB2中创建触发器是数据库管理中的一个重要环节,它允许在特定的数据库操作(如INSERT、UPDATE或DELETE)执行之前或之后自动运行预定义的SQL语句。本文将详细介绍如何在DB2数据库中创建一个触发器,以及触发器的...
【DB2触发器详解】 DB2触发器是数据库管理系统中的一种功能强大的工具,它允许数据库管理员和开发者在特定的数据库操作(如INSERT、UPDATE或DELETE)发生时自动执行一系列操作。这一特性使得触发器成为实现数据完整...
DB2是IBM公司的一款关系型数据库管理系统,广泛应用于企业级数据存储和管理。在DB2的日常运维中,故障诊断是不可或缺的一部分,而db2diag.log日志文件是DB2系统进行故障排查的重要工具。本指南将深入解析db2diag.log...
IBM DB2是一款由IBM公司开发的关系型数据库管理系统(RDBMS),在企业级应用中广泛使用。v9.0版本是DB2的一个重要迭代,它引入了许多新特性以提升性能、安全性和易用性。本套帮助文档主要针对的是SQL(Structured ...
在数据库管理领域,触发器是一种特殊类型的存储过程,它被设计为当特定事件(如数据插入、更新或删除)发生时自动执行。触发器能够帮助维护数据的一致性和完整性,确保业务逻辑的正确执行。本文将深入探讨如何使用...
DB2 是一款功能强大的数据库管理系统,支持多种高级特性如存储过程、触发器以及数据分区等,并且提供了丰富的图形化管理工具。尽管官方文档十分详尽,但对于初次尝试在 Ubuntu 上安装 DB2 的用户来说,可能会遇到...
DB2是IBM公司开发的一款关系型数据库管理系统,广泛应用于企业级数据存储和管理。在DB2 11.5版本中,JDBC(Java Database Connectivity)驱动是连接Java应用程序与DB2数据库的关键组件。JDBC驱动允许Java代码通过...
因此,如果客户端无法连接到DB2服务器,则需要暂时关闭防火墙或对DB2使用端口进行白名单配置。 5. **优化配置**:为了提升DB2的性能,可以设置一些系统参数,比如启用DB2_extended_optimization和DB2_hash_join,...
### DB2 存储过程与触发器详解 #### 标题和描述中的知识点解析 **DB2 存储过程与触发器** 是 IBM DB2 Universal Database(UDB)中的两个重要概念,它们对于构建高性能、高可靠性的数据库应用程序至关重要。 ####...
DB2是IBM公司开发的一款关系型数据库管理系统,广泛应用于企业级数据存储和管理。本压缩包包含DB2数据库的安装包以及链接服务器驱动,对于理解DB2数据库的安装过程和使用至关重要。 首先,我们来详细了解DB2数据库...
本手册主要介绍了DB2数据库系统的命令和配置参数,涵盖了DB2管理服务器、实例、数据库、表、索引、视图、存储过程、触发器、函数等方面的知识点。下面将逐一详细介绍这些知识点。 DB2管理服务器命令 DB2管理服务器...
- DB2支持BEFORE和AFTER两种触发器类型,BEFORE触发器在操作执行前触发,AFTER触发器在操作完成后触发。 - SQL Server同样支持BEFORE和AFTER触发器,但在SQL Server中,BEFORE触发器被称为INSTEAD OF触发器,这...
db2驱动包 db2jcc4.jar db2jcc4.jar db2java.jar db2jcc_javax.jar db2jcc_license_cisuz.jar db2jcc_license_cu.jar db2policy.jar,压缩包无加密,可以放心下载,良心资源。
8. **存储过程和触发器**:支持用户自定义的存储过程和触发器,允许在数据库级别实现复杂的业务逻辑。 在实际操作中,解压并安装这些许可证文件是至关重要的步骤,因为这将决定你能否合法地使用DB2 V9.5的所有功能...
本文将详细介绍如何在DB2中通过触发器及`GENERATED ALWAYS AS IDENTITY`特性来实现字段自增长。 #### 二、使用`GENERATED ALWAYS AS IDENTITY`特性 ##### 2.1 基本语法 在DB2中,可以通过`GENERATED ALWAYS AS ...
"数据库触发器的创建和使用" 数据库触发器是数据库管理系统中的一种特殊类型的存储过程,它可以自动执行某些操作来确保数据的完整性和一致性。本章节将详细介绍触发器的概念、功能、优点、类型,以及如何创建和使用...
本文总结了常用的DB2命令,涵盖数据库创建、连接、表创建、索引创建、视图创建、触发器创建、查询、锁定、FORCE应用程序等多方面。 1. 建立数据库:CREATE DATABASE命令用于创建一个新的数据库,例如:CREATE ...
在您提供的标题和描述中,提到了三个重要的JAR文件:“db2jcc.jar”,“db2jcc_javax.jar”和“db2jcc_license_cu.jar”,它们是DB2 Java JDBC驱动的一部分。 1. **db2jcc.jar**:这是DB2的JDBC网络通信类库,包含...
这里会详细阐述如何在DB2中创建和使用触发器,以及与存储过程的交互。 5. **DB2 SP Training - 5 Advanced SQL.docx**:这部分可能涵盖更高级的SQL技术,比如子查询、联接、窗口函数等,这些在复杂的存储过程中非常...
DB2是IBM公司开发的一款关系型数据库管理系统,广泛应用于企业级的数据存储和管理。在Java应用程序中,为了连接到DB2数据库,我们需要使用特定的驱动程序,这就是db2jcc4.jar和db2jcc_license_cu.jar这两个文件的...