欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  数据库

SQL Server 2005嵌套触发器

程序员文章站 2024-02-15 11:38:46
...

你是公司 A 的数据库管理员。在数据库服务器上只有一个非系统数据库 MyCrmDB。公司新的安全策略要求:在数据库服务器上不允许执行嵌套触发器。为此,你如何确认你的数据库服务器是否满足新安全策略的要求。 在用户定义完整性上,触发器( Trigger )是一种经

你是公司 A 的数据库管理员。在数据库服务器上只有一个非系统数据库 MyCrmDB。公司新的安全策略要求:在数据库服务器上不允许执行嵌套触发器。为此,你如何确认你的数据库服务器是否满足新安全策略的要求。


在用户定义完整性上,触发器(Trigger)是一种经常被使用的方法。微软的SQL Server作为一种关系型数据库,对于触发器也有比较全面的功能支持。触发器是一种特殊类型的存储过程,他的主要作用就是其能够实现主键和外键不能保证的复杂的参照完整性,同时Trigger还可以强化约束,跟踪变化,级联运行和存储过程的调用。在Trigger的类型上SQL Server支持两种类型的触发器:AFTERINSTEDOF。在Trigger的实现方法上SQL Server支持触发器的递归和嵌套。这里我们主要对Trigger的嵌套和递归做实验测试。 一.SQL Server的嵌套和递归 SQL Server可以对使用recursive triggers, nested triggers 分别对Trigger的递归和嵌套做设置, 根据微软的相关资料: RECURSIVE_TRIGGERS:只能限制直接递归,不能限制间接递归。 Nested_triggers : 允许的嵌套层数最多是32层。 我们这里主要实验以下几个问题: 1. RECURSIVE_TRIGGERSnested_triggers的的设置方法。 2. RECURSIVE_TRIGGERSnested_triggers的对于Trigger的影响。 3. Trigger的编写方法和Trigger的实际效果。 二.SQL Server的嵌套和递归的实现 1. RECURSIVE_TRIGGERS 的设置: 其指令如下: ALTER DATABASE ‘Emp_Struct’ SET RECURSIVE_TRIGGERS OFF(ON); Recursive triggers: ON: 设置递归开启 OFF: 设置递归为关闭 2. Nested triggers 的设置: 其指令如下: EXEC sp_configure 'nested_triggers', 1(0) RECONFIGURE WITH OVERRIDE Nested triggers: 1 : 设置嵌套开启 0 : 设置嵌套关闭
如果一个触发器在执行操作时引发了另一个触发器,而这个触发器又接着引发下一个触发器……这些触发器就是嵌套触发器。触发器可嵌套至 32 层,并且可以控制是否可以通过"嵌套触发器"服务器配置选项进行触发器嵌套。

如果允许使用嵌套触发器,且链中的一个触发器开始一个无限循环,则超出嵌套级,而且触发器将终止。

可使用嵌套触发器执行一些有用的日常工作,如保存前一触发器所影响行的一个备份。例如,可以在 titleauthor 上创建一个触发器,以保存由 delcascadetrig 触发器所删除的 titleauthor 行的备份。在使用 delcascadetrig 时,从 titles 中删除title_id PS2091 将删除 titleauthor 中相应的一行或多行。要保存数据,可在 titleauthor 上创建 DELETE 触发器,该触发器的作用是将被删除的数据保存到另一个单独创建的名为 del_save 表中。例如:

CREATE TRIGGER savedel
   ON titleauthor
FOR DELETE
AS
   INSERT del_save
   SELECT * FROM deleted

不推荐按依赖于顺序的序列使用嵌套触发器。应使用单独的触发器层叠数据修改。


说明  由于触发器在事务中执行,如果在一系列嵌套触发器的任意层中发生错误,则整个事务都将取消,且所有的数据修改都将回滚。在触发器中包含 PRINT 语句,用以确定错误发生的位置。


递归触发器
触发器不会以递归方式自行调用,除非设置了 RECURSIVE_TRIGGERS 数据库选项。有两种不同的递归方式: 

直接递归 
即触发器激发并执行一个操作,而该操作又使同一个触发器再次激发。例如,一应用程序更新了表 T3,从而引发触发器 Trig3。Trig3 再次更新表 T3,使触发器 Trig3 再次被引发。

间接递归 
即触发器激发并执行一个操作,而该操作又使另一个表中的某个触发器激发。第二个触发器使原始表得到更新,从而再次引发第一个触发器。例如,一应用程序更新了表 T1,并引发触发器 Trig1。Trig1 更新表 T2,从而使触发器 Trig2 被引发。Trig2 转而更新表 T1,从而使 Trig1 再次被引发。

当将 RECURSIVE_TRIGGERS 数据库选项设置为 OFF 时,仅防止直接递归。若要也禁用间接递归,请将 nested triggers 服务器选项设置为 0。

示例
A. 使用递归触发器解决自引用关系
递归触发器的一种用法是用于带有自引用关系的表(亦称为传递闭包)。例如,表 emp_mgr 定义了: 

一个公司的雇员 (emp)。


每个雇员的经理 (mgr)。


组织树中向每个经理汇报的雇员总数 (NoOfReports)。 
递归 UPDATE 触发器在插入新雇员记录的情况下可以使 NoOfReports 列保持最新。INSERT 触发器更新经理记录的 NoOfReports 列,而该操作递归更新管理层向上其它记录的 NoOfReports 列。

USE pubs
GO
-- Turn recursive triggers ON in the database.
ALTER DATABASE pubs
   SET RECURSIVE_TRIGGERS ON
GO
CREATE TABLE emp_mgr (
   emp char(30) PRIMARY KEY,
    mgr char(30) NULL FOREIGN KEY REFERENCES emp_mgr(emp),
    NoOfReports int DEFAULT 0
)
GO
CREATE TRIGGER emp_mgrins ON emp_mgr
FOR INSERT
AS
DECLARE @e char(30), @m char(30)
DECLARE c1 CURSOR FOR
   SELECT emp_mgr.emp
   FROM   emp_mgr, inserted
   WHERE emp_mgr.emp = inserted.mgr

OPEN c1
FETCH NEXT FROM c1 INTO @e
WHILE @@fetch_status = 0
BEGIN
   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Add 1 for newly
   WHERE emp_mgr.emp = @e                            -- added employee.

   FETCH NEXT FROM c1 INTO @e
END
CLOSE c1
DEALLOCATE c1
GO
-- This recursive UPDATE trigger works assuming:
--   1. Only singleton updates on emp_mgr.
--   2. No inserts in the middle of the org tree.
CREATE TRIGGER emp_mgrupd ON emp_mgr FOR UPDATE
AS
IF UPDATE (mgr)
BEGIN
   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Increment mgr's
   FROM inserted                            -- (no. of reports) by
   WHERE emp_mgr.emp = inserted.mgr         -- 1 for the new report.

   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports - 1 -- Decrement mgr's
   FROM deleted                             -- (no. of reports) by 1
   WHERE emp_mgr.emp = deleted.mgr          -- for the new report.
END
GO
-- Insert some test data rows.
INSERT emp_mgr(emp, mgr) VALUES ('Harry', NULL)
INSERT emp_mgr(emp, mgr) VALUES ('Alice', 'Harry')
INSERT emp_mgr(emp, mgr) VALUES ('Paul', 'Alice')
INSERT emp_mgr(emp, mgr) VALUES ('Joe', 'Alice')
INSERT emp_mgr(emp, mgr) VALUES ('Dave', 'Joe')
GO
SELECT * FROM emp_mgr
GO
-- Change Dave's manager from Joe to Harry
UPDATE emp_mgr SET mgr = 'Harry'
WHERE emp = 'Dave'
GO
SELECT * FROM emp_mgr
GO

以下是更新前的结果:

emp                            mgr                           NoOfReports
------------------------------ ----------------------------- -----------
Alice                          Harry                          2
Dave                           Joe                            0
Harry                          NULL                           1
Joe                            Alice                          1
Paul                           Alice                          0

以下为更新后的结果:

emp                            mgr                           NoOfReports
------------------------------ ----------------------------- -----------
Alice                          Harry                          2
Dave                           Harry                          0
Harry                          NULL                           2
Joe                            Alice                          0
Paul                           Alice                          0