加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

SqlServer_触发器相关信息;

发布时间:2020-12-12 13:20:17 所属栏目:MsSql教程 来源:网络整理
导读:1、定义 触发器是一种特殊类型的存储过程,该存储过程响应特定的事件。 2、类型 (1)、数据定义语言(Data Definition Language,DDL)触发器; (2)、数据操作语言(Data Manipulation Language,DML)触发器; 3、语法 CREATE TRIGGER triggerName ON [schema name
1、定义 触发器是一种特殊类型的存储过程,该存储过程响应特定的事件。 2、类型 (1)、数据定义语言(Data Definition Language,DDL)触发器; (2)、数据操作语言(Data Manipulation Language,DML)触发器; 3、语法 CREATE TRIGGER <triggerName> ON [<schema name>.]<table or view name> [WITH ENCRYPTION | EXECUTE AS <CALLER | SELF | <user> >] {{{FOR|AFTER} <[DELETE] [,] [INSERT] [,] [UPDATE]>} |INSTEAD OF} [WITH APPEND] [NOT FOR REPLICATION] AS < <sql statements> | EXTERNAL NAME <assembly method specifier> > 可以看到,有非常熟悉的 CREATE<objecttype><objectname>以及在其他许多对象中可以看到的执行语句——这里只是加入了 ON 子句来指出触发器将要加在其上的表,以及在何时和何种情况下激活这个触发器。 4、ON 子句 ??? 这部分只是对创建触发器的对象进行命名。记住,如果触发器的类型是 AFTER 触发器(如果使用 FOR 或 AFTER 来声明触发器),那么 ON 子句的目标就必须是一个表——AFTER触发器并不支持视图。 5、WITHENCRYPTION 子句 ??? 该子句和在视图与存储过程中的工作方式一样。如 果加上了这个选项,则可以确保没有人能够看到您的代码(甚至是您自己!)。如果要建立贸易销售软件,或者关心安全而并不想用户看到所修改或访问的数据,那 么这个选项是非常有用的。很明显,如果想以后重新创建触发器,那么需要保存这个代码的副本,这样在其他地方可以用它来创建触发器。 6、FOR|AFTER 子句与 INSTEADOF 子句 ??? 除了要决定激活触发器(INSERT、 UPDATE 和/或 DELETE)的查询类型以外,还要对激活触发器的定时时间作出选择。虽然可以使用长期接触的 FOR 触发器(也可以选择性地使用关键字 AFTER 来替换),而且这也是人们经常考虑的一种触发器,但是也可以使用INSTEAD OF 触发器。对这两个触发器的选择将影响到是在修改数据之前还是之后来输入触发器。在这两种情况中,在将任何修改提交给数据库之前,您都将在触发器中。 ??? 这里需要注意的是,无论进行何种选择,SQL Server 会将两张工作表放在一起——其中的 INSERTED 表保存插入记录的副本,另一张 DELETED 表保存删除的任何记录的副本。 ??? 现在需要认识到,使用 INSTEAD OF 触发器,创建这两张工作表是发生在检查任何约束之前,而使用 FOR 触发器,这些表的创建是发生在检查约束之后。使用 INSTEAD OF 触发器的重点在于可以在用户请求的任何地方运行您自己的代码。这意味着可以在视图中清除任何不确定的插入问题(回想一下第 10 章中,当在视图中有 JOIN 操作的时候进行插入的问题)。这也意味着在检查约束之前可以采取行动清除违 反约束的情况。提示:这听上去很不 错,但是它确实非常复杂。这意味着需要预见每一种可能性。另外,这还意味着向每一个查询中有效地增加一次预处理,它会使用任何方式修改表中的数据(这对于 性能来说不是件好事)。虽然听上去不错,但是这里把 INSTEAD OF 触发器归类为相当高级的类别。 ??? 使用 FOR 和 AFTER 声明的触发器在行为上是一样的。它们与 INSTEAD OF 触发器最大的区别在于它们是在检查完约束之后建立工作表的。FOR(或者可以选择性地使用 AFTER)子句指明想要在哪种动作下激活触发器。每当有INSERT、UPDATE 或 DELETE 或三者的混合时,都可以激活触发器。FOR 子句可以如下所示:FOR INSERT,DELETE或者是:FOR UPDATE,INSERT或者是:FOR DELETE; 7、INSERT 触发器 ??? 每当有人向表中插入全新的数据行时,都会执行在代码中通过 FOR INSERT 标记声明的触发器的代码。对于插入的每一行来说,SQL Server 会创建该新行的副本并把它插入到一个特殊表中,该表只在触发器的作用域内存在。该表称为 INSERTED。需要理解的是,INSERTED 表的生存期和触发器一样长。在触发器开启之前或者触发器完成之后,都要认为它是不存在的。 8、DELETE 触发器 ??? 它和 INSERT 触发器的工作方式相同,除了 INSERTED 表会变空之外(毕竟,是进行删除而非插入,所以对于 INSERTED 表来说,没有记录)。每个删除的记录的副本将插入到另一个表中,该表 称为 DELETED。和 INSERTED 表类似,该表将只限于触发器的作用域范围内。 9、 UPDATE 触发器 ??? 除了一些改变以外,它和前面的触发器是很类似的。每当对表中现有的记录进行修改时,都会激活在触发器中声明为 FOR UPDATE 的代码。这里的改变是指没有表UPDATED。SQL Server 会把每一行当成好像删除了现有的记录,并插入了全新的记录一样。可能和您猜想的一样,声明为 FOR UPDATE 的触发器包含了不只一个表,而是两个特殊的表,称为 INSERTED 和 DELETED。当然,这两个表有完全相同数量的数据行。 10、WITH APPEND 子句 ??? WITHAPPEND 比较古怪,老实讲,可能不会使用它;然而,这里为了以防万一,将对它进行介绍。WITHAPPEND 只能应用于 6.5 兼容性模式中(可以使用 sp_dbcmptlevel 设置)。 ??? SQL Server 6.5 以及先前的版本不允许在单个表上使用相同类型的多个触发器。例如,如果已经声明了一个称为 trgCheck 的触发器来强制在更新和插入时执行数据完整性,那么不能创建另一个触发器来进行级联更新。一旦创建了更新(或插入、删除)触发器,那么就不能创建相同动作类型的另一个触发器。 ??? 随着 SQL Server 7.0 的出现,这些规则也极大地改变。不再需要担心已有的相同动作查询类型的触发器数量,可以根据需要创建这些触发器。但是在 6.5 兼容模式下运行数据库时会碰到问题——数据库仍然会按照对于给定的表只能有一个给定类型的触发器的概念来运行。 ??? WITHAPPEND 子句显式告诉 SQL Server,即使在表上已经有了这种类型的触发器,还可以增加一个新的触发器,从而解决这个问题——当有合适的动作(INSERT、UPDATE、DELETE)发生时,会激活两个触发器。这是一箭双雕的方法。 11、NOTFOR REPLICATION 子句 ??? 如果增加了该选项,则会稍微地改变何时激活触发器的规则。在适当的位置使用这个选项,每当与复制相关的任务修改表的时候,都不会激活触发器。通常,当修改了原始表并且不会再进行修改的时候会激活触发器。 12、AS 子句 和在存储过程中的使用相同,这里面是问题的实质。AS 关键字告诉 SQLServer 将要启动代码。 一、为了数据完整性规则使用触发器 ??? 虽然这不会成为首要的选择,但是触发器也同样可 以完成像 CHECK 约束甚至是DEFAULT 约束一样的功能。“使用触发器还是 CHECK 约束?”这个问题的答案是相当明显的:“看情况而定”。如果 CHECK 约束可以完成任务,那么可能它是更好的选择。但是,有时会出现 CHECK 约束不能完成任务的情况,或者是当触发器比从 CHECK 过程继承来的内容更满足要求的时候。想要使用触发器而非 CHECK 约束的示例包括:业务规则需要引用单个表中的数据;业务规则需要检查更新的增量(更新前后的区别);需要一个定制的错误消息; 1、处理来自于其他表的需求 示例:Northwind 数据库,Products 和 Order Details 表, CREATE TRIGGER OrderDetailNotDiscontinued ?? ?ON [Order Details] ?? ?FOR INSERT,UPDATE AS ?? ?IF EXISTS ?? ?( ?? ??? ?SELECT'True' ?? ??? ?FROM Inserted i ?? ??? ?JOIN Products p ?? ??? ?ON i.ProductID = p.ProductID ?? ??? ?WHERE p.Discontinued= 1 ?? ?) ?? ?BEGIN ?? ??? ?RAISERROR('OrderItemis discontinued.TransactionFailed.',16,1) ?? ??? ?ROLLBACK TRAN ?? ?END 记住,如果需要,还可以创建引发的定制错误消息,而不是用于 RAISERROR 命令的特殊的消息。 2、使用触发器来检查更新的增量 ??? 有时,您不会对值为多少和值改变了多少感兴趣。虽然没有列或表来告诉您这些信息,但是可以在触发器中使用 INSERTED 和 DELETED 表来计算它。 示例:产品库存量每次减少不能超过库存的一半,如果这两个条件都满足(对库存的减少而不是增加将超过 50%),那么将激活一个错误。 CREATE TRIGGER ProductIsRationed ?? ?ON Products ?? ?FOR UPDATE AS ?? ?IF EXISTS ?? ?( ?? ??? ?SELECT'True' ?? ??? ?FROM Inserted i ?? ??? ?JOIN Deleted d ?? ??? ?ON i.ProductID = d.ProductID ?? ??? ?WHERE (d.UnitsInStock- i.UnitsInStock) > d.UnitsInStock /2 ?? ??? ?AND d.UnitsInStock- i.UnitsInStock>0 ?? ?) ?? ?BEGIN ?? ??? ?RAISERROR('Cannot reduce stock by more than 50%% at once.',1) ?? ??? ?ROLLBACK TRAN ?? ?END 注意在 RAISERROR 中使用了两个%符号,而不是一个。记住,一个%符号将作为参数的占位符,所以一个%符号本身将不会出现在错误消息中。通过在一行中放置%%,SQL Server就能知道确实需要输出一个百分号。 3、 将触发器用于自定义的错误消息 ??? 我们已经在一些示例中接触了这个内容,但是记住,当想要控制传给用户或客户应用程序的错误消息或错误数量时,使用触发器是很方便的。 ??? 例如,使用 CHECK 约束,将会得到标准的 547 错误,以及没有什么特征的解释。通常,对于想知道具体错误的用户来说,这是无用的信息——确实,客户端应用程序经常没有足够的信息,从而能为用户作出更智能和更有帮助的响应。 ??? 简而言之,当已经有一些可以完成数据完整性的内容,但是不能提供足够信息进行处理的时候,需要创建触发器。 二、触发器的其他用途 ??? 除了直接的数据完整性用途以外,触发器还有一些其他的用途,如更新概述信息;放入非规范化表中以提供报表;设置条件标志; 三、其他触发器的问题 ??? 我们已经了解了大部分的内容,但是如果您觉得已经完成了触发器的学习,那么请您再思考一下。前面已经提到过,需要考虑触发器不好的地方。下面将指出需要考虑的最重要的问题,还会提供一些关于触发器附加功能和可能性的信息。 1、触发器可以嵌套 ??? 嵌套的触发器是指那些不是由发出的语句直接激活的、而是由另一个触发器发出的语句激活的触发器。 ??? 触发器可以激活的深度取决于:嵌套的触发器是否在系统中打开(这是系统级的、非数据库级的选项,可以使用 ManagementStudio 或 sp_configure 来设置,默认是打开的);是否有嵌套 32 级深度的限制;触发器是否已经激活,触发器默认地只能在每个触发器事务中激活,一旦激活,则它会忽略其他任何调用,这些调用将作为相同触发器动作的一部分,一旦执行一条全新的语句(即使在同一个完整的事务中),则处理会重新开始; 2、触发器可以递归 ??? 什么是递归的触发器?递归触发器是指会在最后激活相同触发器的触发器。它可以直接触发(通过在设置触发器的表上完成动作查询),也可以间接触发(通过嵌套过程)。 ??? 递归触发器很少见。实际上,递归触发器默认是关闭的。但是,这是一种处理上述情况,也就是嵌套触发器并且希望第二次发生更新的一种方式。递归和嵌套不同,它是数据库级的选项,可以使用 sp_dboption 系统存储过程来设置。 ??? 递归触发器的威胁在于可能会陷入某种无意识的循环中。同样,需要确保在需要的时候可以通过检查递归的形式来停止过程。 3、触发器不能防止修改体系结构 ??? 这是典型的好消息和坏消息。为了使修改体系结构变得容易,使用触发器很有用。我通常在开发周期的早期(当更有可 能对数据库的设计进行修改时)为了引用完整性而使用触发器,并且当要完成的时候在后期转变为DRI(声明引用完整性(declarative referential integrity); ??? 当您想要删除表并且使用 DRI 重新创建它的时 候,必须在删除该表之前先删除所有的约束。这会使得在删除多个约束、进行修改、再次加入约束方面变得复杂。尝试确保那些应该删除的内容都删除,从而修改的 脚本可以运行的做法相当麻烦。确保所有需要的一切回到以前的状态也很麻烦。触发器可以做到所有这一切,因为它不关心任何改变直到它确实运行。 ??? 但这也有些困难——问题是它们何时运行。可以看到,这意味着需要修改体系结构并且破坏一些触发器,甚至您并不知道您已经这么做了。直到这些触发器第一次解决问题中的对象时,才会发现这种做法的错误之处。到了那时,可能会觉得很难再去对所作所为以及原因进行修补。这两方面都有争论——不管使用的是哪种方法,只要记住这些争论即可。 4、可以关闭触发器而不删除它 ??? 有时,像 CHECK 约束那样,想要关闭完整性的功能来做一些违反约束的事情,但是会有正当的理由这么做(数据输入是最常见的理由)。 ??? 另一个这么做的理由发生在如下情况中:要执行某些批量插入,但是已经 100%确认插入的数据有效。在这种情况下,可能想关闭触发器,从而消除系统开销并提高插入速度。 >语法:可以使用 ALTERTABLE 语句来关闭触发器,语法如下, ALTERTABLE<tablename> <ENABLE|DISABLE>TRIGGER<ALL|<triggername>> 您可能会想到,这里最需要注意的是,“不要忘了使触发器重新有效”。最后要做的事情是:如果关闭了触发器是为了做批量数据输入,那么本书建议踢掉所有的用户并进入单用户模式、或 dbo-only 模式,或这两种模式。那么当关闭触发器时,这会保证不会漏过任何用户。 5、触发器激活顺序 ??? 在 SQL Server 以前的版本中(7.0 以及之前的版本),没有对激活顺序的控制。可能您会说在 7.0 之前确实只有一种特殊类型的触发器(INSERT、 UPDATE、DELETE),所以激活的顺序是个讨论的热点问题。SQL Server 后面的版本提供了对触发器顺序的有限控制。对于任何给定的表(不是视图,因为只有 AFTER 触发器才可以指定激活顺序,而视图只接受INSTEAD OF 触发器),可以指定某个触发器(并且是唯一的一个)优先激活。同样,可以选择一个触发器(唯一的一个)最后激活。其他所有的触发器之间没有什么激活顺 序——也就是说,除了能保证一个激活顺序为 none 的触发器是在 FIRST 触发器(如果有的话)完成之后以及在 LAST 触发器(如果有的话)开始之前激活的以外,不能保证它以哪种顺序激活。第一个和最后一个触发器的创建和其他任何触发器的创建相同。在创建完触发器之后使用一个特殊的系统存储过程sp_settriggerorder 来声明激活顺序的首选项。 sp_settriggerorder语法: sp_settriggerorder[@triggername=] '<trigger name>',[@order=] '{FIRST|LAST|NONE}',[@stmttype=] '{INSERT|UPDATE|DELETE}' ??? 这里,对于任何特殊操作(INSERT、UPDATE、DELETE)来说,只能有唯一的第一个触发器。同样,对于任何特殊操作来说,只能有唯一的最后一个触发器。可以把任何其他的触发器看作是 none——也就是说,没有特殊激活顺序的触发器的数量没有限制。 ??? 所以有一个问题:“为什么要注意激活顺序?”通常情况下完全不需要注意这个问题。在其他时候,这可能是重要的逻辑上的或者只是一个使性能更好的想法。这里看一下细节的内容。 (1)、出于逻辑原因而控制激活顺序 ??? 为什么需要在激活一个触发器之前去激活另一个触发器?最常见的理由是第一个触发器是后面触发器的基础或是它验证了后面的触发器。在 SQL Server 6.5 和以前的版本中,不需要过多地去考虑这些事情——因为对于一个给定的表来说只允许一种特殊类型的触发器(UPDATE、DELETE、 INSERT)。这意味着不可能在触发器之前发生另一个触发器。因为您把所有的逻辑都放入了一个触发器中,所以就在代码的开始处放入先要发生的事情,在最后处放入最后要发生的事情(道理并不复杂)。 ??? 7.0 版本和以前的版本相比有好有坏。不需要再 把所有的逻辑放入一个触发器中。这非常了不起,因为这意味着可以对逻辑上有区别的触发器代码部分进行物理地分离,这样会使得代码更易于管理,并允许在当代 码的一些部分继续运行的时候,使得另一些部分的代码无效(还记得前面章节中的 NO CHECK 吗?)。最后阶段是这样的,如果把代码按照这种方式分离,那么当代码只在一个触发器中时,将失去该代码的逻辑步骤的顺序。 ??? 通过获得至少是对激活顺序的基础层面的控制,现在就有最好的结果了——可以逻辑地去分离触发器,但是仍然保持了哪些代码首先或者最后运行的必要的优先顺序。 (2)、出于性能原因而控制激活顺序 ??? 在性能方面,FIRST 触发器是唯一进行非常处 理的触发器。如果有多个触发器,但是其中只有一个可能会产生回滚(例如,它会强制执行约束所不能处理的复杂的数据完整性规则),那么就需要考虑标记这个触发器为 FIRST 触发器。这会在事务采取任何其他动作之前保证已经完成了最有可能发生回滚的触发器。在检测到回滚之前进行的处理越多,需要回滚的内容也越多。在执行额外动作前确定发生回滚的最高可能性。 四、INSTEAD OF 触发器 ??? INSTEAD OF 触发器是 SQL Server 2000 新增的触发器,它也是 SQL Server 中最为复杂的功能。虽然这不在初学范围内,但是本书认为初学者需要学习可获得的知识,所以这里介绍它的内容。 ??? 本质上说,INSTEAD OF 触发器是一个可以用来作为拦截者的代码块,它会拦截任何人对表或视图进行的操作。这里可以选择继续下去并执行用户请求的动作,或者是执行完全不同的动作。 ??? 像普通的触发器一样,INSTEAD OF 触发器有三种:INSERT、UPDATE 和 DELETE。每一种触发器的最为常见的用途是相同的——当正在处理基于多个表的视图时解决哪个表获得真正修改的多义性。 五、性能考虑 ??? 前面已经看过了触发器的优点和缺点。它最大的缺点来自于那些优化论者——他们热爱这个理论并且想处理所有的一切,或者他们能够说出触发器的灵活之处并想把它们用于每一件事情中。 ??? 这里想说的是,和本章开始说的那样,当应该使用触发器的时候使用它们。如果这听上去不明确,那么就好了!在很少情况下编程是黑白分明的事情,数据库也是如此。可是,这里将会列出一些事实供您思考。 1、 触发器是反应性的而非主动的 ??? 这里的意思是触发器在事实之后发生。当激活触发器时,整个查询已经运行并且事务也已经记录到日志中(但是在激活触发器的那点的语句处没有提交)。这是指,如果触发器要回滚,那么触发器必须撤消已经做的所有工作。总之,请记住这点。这样带来的影响有多大取决于查询的大小。 ??? 那这又有什么问题呢?您可能会问。这里将它与约 束的概念作一下比较,约束是主动的——也就是它们在语句真正执行前发生。这意味着它会检测导致失败的操作,并且在过程的早期防止。这通常意味着会运行得快 一些——在更为复杂的查询中速度可能更快。注意,这个额外的提速只是说明了当回滚发生的时候对任何重要盘区的提速。 ??? 最后的分析结果是,如果正在处理少量回滚和/或 复杂性以及影响语句的运行时间变慢的情况,那么触发器和约束之间没有太大的区别。可能有一些,但不是很多。可是,如果回滚的数量不可预知或者会很大的时 候,则需要坚持使用约束(本书也建议使用约束,除非您有特殊的理由不这么做)。 2、触发器与激活它们的过程之间不存在并发问题 ??? 您可能已经注意到在本章中经常会使用 ROLLBACK 语句,即使不发出 BEGIN TRAN 命令。这是因为触发器总是导致触发器激活的语句同一事务的隐式部分。 ??? 如果激活的语句不是显式事务(有 BEGIN TRAN 的事务)的一部分,那么它仍然是一条语句事务的一部分。在这种情况下,在触发器内部发出的 ROLLBACK TRAN 仍然会回滚整个事务。 ??? 这个相同事务一部分的另一个结果是触发器继承的锁已经在事务上打开了,这个触发器是事务的一部分。这意味着不需要做任何特殊的处理来避免碰到事务中由其他语句所创建的锁。在事务的作用域内可以自由访问,并且能发现基于修改的数据库已经被事务中先前的语句所替代。 3、使用 IF UPDATE()和 COLUMNS_UPDATED() ??? 在 UPDATE 触发器中,经常会限制运行在触发器中代码的总量,这些代码会检查我们感兴趣的列是否已经修改过了。为了做到这点,可以使用 UPDATE()或 COLUMNS_UPDATE()函数。下面将分别介绍这两个函数。 (1)、UPDATE()函数 ??? UPDATE()函数只在触发器的作用域内适用。它唯一的目的是用来提供一个布尔响应(真/假),来说明一个特殊列是否已经更新。使用这个函数可以决定一个特殊的代码块是否需要运行——例如,代码是否只和更新的特殊列相关。 ALTER TRIGGER ProductIsRationed ?? ?ON Products ?? ?FOR UPDATE ?? ?AS ?? ?IF UPDATE(UnitsInStock) ?? ?BEGIN ?? ??? ?IF EXISTS ?? ??? ?( ?? ??? ??? ?SELECT 'True' ?? ??? ??? ?FROM Inserted i ?? ??? ??? ?JOIN Deleted d ?? ??? ??? ?ON i.ProductID = d.ProductID ?? ??? ??? ?WHERE (d.UnitsInStock- i.UnitsInStock) > d.UnitsInStock /2 ?? ??? ??? ?AND d.UnitsInStock- i.UnitsInStock>0 ?? ??? ?) ?? ??? ?BEGIN ?? ??? ??? ?RAISERROR('Cannot reducestock bymorethan50%% at once.',1) ?? ??? ??? ?ROLLBACK TRAN ?? ??? ?END ?? ?END 通过修改,现在限制了剩下来的代码只能在已经更新了 UnitsInStock 列的时候运行。用户可以修改任何其他列的值,此处不用去关心这些列。这意味着可以执行更少的代码行,而且触发器会比先前的版本运行地更好。 (2)、COLUMNS_UPDATED()函数 ??? 这个函数和 UPDATE()运行方式有点不同,但是大体目的相同。COLUMNS_UPDATED()函数给了我们在同一时刻检查多个列的能力。为了做到这点,函数使用了位掩码,位掩码将 varbinary 数据的一个或多个字节与表中的列相关。 ??? 使用这些信息意味着需要将所有字节的二进制值累加起来,并要考虑到最左面的数字是最不重要的。所以,如果需要考虑比较 2、5、7,那么需要把每一位的二进制值累加起来:2+16+64。然后需要使用位操作符来对列的二进制值的总和与位掩码作比较:|表示按位OR;&表示按位AND;^表示按位ExclusiveOR; >示例:想像更新含有 5 个列的表的情况,如果更新了第 1、第 3 和第 5 列,那么COLUMNS_UPDATED 使用的位掩码是 10101000,1+4+16=21。这里可以使用:COLUMNS_UPDATED()> 0 来找出是否更新了列;COLUMNS_UPDATED()^ 21 = 0? 来找出是否更新了所有指定的列( 本例中为1、3、5),但是没有更新其他列;COLUMNS_UPDATED() & 21 = 21? 找出是否更新了所有指定的列,而其他列的状态则不会有所影响;COLUMNS_UPDATED | 21 != 21 找出是否更新了除了我们感兴趣的列以外的列;切记,需要了解的是,布尔代数是比较难的——布尔代数对很多人来说并不是很容易理解的概念,所以需要仔细地检验并不断地尝试,尝试,再尝试! 4、使触发器简短 ??? 这里说的很明确,但是也有必要这么做。我经常会在一些其他人写的存储过程和触发器中看到一些过于庞大而且愚蠢的代码。我不知道人们为什么如此急着这么做,或者这些人认为他们使用的方法比较快速,所以这样做没有关系。 ??? 记住触发器总是激发触发器的语句同一事务的隐式 部分。这意味着语句直到触发器完成后才完成。考虑一下——如果在触发器中写入运行时间很长的代码,那么这将意味着,创建的每一段会激活触发器的代码会依次 运行很长时间。这会在想要指出代码运行这么长时间的原因方面造成不便。您写的存储过程看上去效率很高,但是实际运行起来很糟糕。这可能要用几周的时间去寻 找问题,但可能永远不能找出存储过程存在的问题——因为是它激活的触发器有问题。 5、选择索引时不要忘记触发器 ??? 这是另一个常见的错误。检查所有的存储过程和视图以发现最好的索引的混合——但是完全忘记了已经在触发器中运行了重要的代码。这也是上一节中相同的概念——长时间运行的查询导致长时间运行的语句,这样依次会导致长时间运行所有的一切。当进行优化的时候不要忘了优化触发器。 6、不要在触发器中进行回滚 ??? 这部分内容比较难懂,因为回滚是触发器经常需要完成的一个重要的部分。记住,AFTER 触发器(它和普通类型的触发器有很大区别)在所有工作已经完成后发生——这意味着回滚的代价是昂贵的。这里 DRI 将获得所有的性能优势。如果在触发器中使用很多 ROLLBACK TRAN 语句,那么请确保在运行会激活触发器的语句前进行预处理错误检查。也就是说,因为 SQL Server 在这种情况下不是主动的,而是反应性的,所以需要事先地去检查错误,而不是等待回滚。 六、删除触发器 ??? 删除触发器就和删除其他对象一样简单:DROP TRIGGER <triggername>; 七、调试触发器 ??? 如果想要用调试存储过程(见第 12 章)和函数的方法来调式触发器,那么您会后悔莫及的——您找不到这种方法。虽然触发器调试是件痛苦的事情,但是本书不会说“不行”这样的答案,这里让调试器为我们工作——虽然不是很好,但是能运行。 ??? 基本上,要做的就是去创建一个会激活需要调试的触发器的包装器过程。它本质上是存储过程,唯一的目的是使用调试器提供发出进入触发器的语句的方式。 ??? 为了说明这点,这里要使用到本章中的测试代码的最后一部分,把它放入到存储过程中,这样就可以在调式器中观察每一步的执行情况: ALTERPROC spTestTriggerDebugging AS BEGIN -- Thisone should work UPDATEProducts SETUnitsInStock= UnitsInStock- 1 WHEREProductID = 6; -- Thisone shouldn't UPDATEProducts SETUnitsInStock= UnitsInStock- 12 WHEREProductID = 26; END 后记: ??? 触发器是非常有用的工具,它能给数据完整性以及 系统的整体操作带来巨大的灵活性。这就是说,要重视触发器。如果使用触发器进行数据的求和,触发器会极大地提高系统的性能,但是它也会成为祸害,因为很难 对它们进行调试(即使已经有了调试器),一个编写糟糕的触发器不但会影响自己,还会影响激活该触发器的语句。 ??? 在对触发器感到灰心丧气之前,或者在对这些触发器模板感到烦心之前,请记住有很多的工具将帮助您自动生成满足一定要求的触发器。本书会在附录 C 中对这些工具作进一步介绍。 ??? 到现在为止,已经学习了大部分的内容,这里将开始外围知识的学习。也就是说,将要开始学习 SQL Server 以外的知识。这里并不是说后面将要介绍的内容是 RDBMS 系统以外的内容,而是并不需要这些知识来构成 SQL 的功能。毕竟,SQL Server 包含的内容太多,很难把所有的内容都压缩到一本书中进行介绍。 ??? 本章将从介绍这些额外内容的背景开始,接着将介绍 SQL Server 支持 XML 的一些特性。这里的问题在于 XML 是完全独立的内容,它和到现在为止接触的关系系统有着很大的区别。为什么 SQL Server 要包含这么多的功能来支持它呢?简要的回答是,随着数据仓库的出现,对数据来说,XML 可能最为重要。 ??? XML 的出现已经有好几年,但是由于争议比较大,它的实际应用没有达到应有的程度。大约从 2000 年开始,XML 的应用范围越来越广,它将作为一种使实时数据更新服务(data feed)以及大小合理的数据文档可用的通用方式。XML 提供了数据自描述的方法——也就是说,可以定义与 XML 文档一起的类型和确认信息,这样不论用户是谁(即使他们连如何连接SQL Server 都不知道),他们都能够理解该数据的规则。XML 通常不是存储数据的好地方,但它确实是使得数据有用的一种引人注目的方式——同样地,使用 XML 的方法也在不断地发展。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读