• 沒有找到結果。

触发器是一种特殊的存储过程,当在指定的数据表进行插入、修改或删除行操作时被自 动调用。触发器为数据提供了有效的监控和处理机制,确保数据和业务的完整性。SQL Server 2005 数据库在传统的触发器基础上进行了扩展,实现了对数据库结构操作时的触发机制(DDL 触发器)。

7.4.1 触发器概述 1.触发器的概念

触发器(Trigger)是用户对某一表中的数据做插入、更新和删除操作时被触发执行的一段 程序,通常我们使用触发器来检查用户对表的操作是否符合整个应用系统的需求以及是否符合 商业规则,以维持表内数据的完整性和正确性。

触发器和存储过程一样是由 Transact-SQL 语句写成的程序。存储过程是由用户利用 EXECUTE 命令执行它,但触发器是在用户对触发对象进行操作时被触发执行的。

触发器的功能和表内设置的一些列限制(Constraints)有些重叠,事实上如果列限制的功 能能够达到应用程序要求的话,应该无须用额外的触发器来做相关的工作。不过触发器可在需 要参考到其他数据库内的数据来做检查时使用,另外,如果要做比较复杂的安全措施,例如:

将操作某一表的用户的名字和时间记录到另外一表的话,使用列限制的方式就无法做到,而触 发器能做到。

2.与触发器有关的两个特殊表

SQL Server 为每个触发器创建了两个专用临时表:INSERTED 表和 DELETED 表。这是 两个逻辑表,由系统来维护,不允许用户直接对这两个表进行修改。它们存放于内存中,而不 是存放在数据库中。这两个表的结构总是与被该触发器作用的表的结构相同。触发器工作完成 后,与该触发器相关的这两个表也会被删除。

(1)INSERTED 表:存放由于 INSERT 或 UPDATE 语句的执行而导致要加到该触发器作 用的表中去的所有新行。即把插入或更新表的新行值,在插入或更新表的同时,也将其副本存 入 INSERTED 表中。

(2)DELETED 表:存放由于 DELETE 或 UPDATE 语句的执行而导致要从被该触发器作 用的表中删除的所有行。即把被作用表中要删除或要更新的旧值移到 DELETED 表中。

对于 INSERT 操作,只在 INSERTED 表中保存插入行的新值,而 DELETED 表中无数据。

对于 DELETE 操作,只在 DELETED 表中保存被删除行的旧值,而 INSERTED 表中无数据。

对于 UPDATE 操作,可以将它考虑为 DELETE 操作和 INSERT 操作的结果,所以在 INSERTED 表中存放着更新后的新值,DELETED 表中存放着更新前的旧值。

3.触发器的分类

SQL Server 2005 提供两大类触发器:DML 触发器和 DDL 触发器。

(1)DML 触发器:DML 触发器是当数据库中发生数据操纵语言(DML)事件时要执行 的操作。DML 事件包括对表或视图发出的 INSERT、UPDATE、DELETE 语句。DML 触发器 包括两种类型:AFTER 触发器和 INSTEAD OF 触发器。下面将会对这两种触发器作详细介绍。

(2)DDL 触发器:DDL 触发器是 SQL Server 2005 新增的功能。它是一种特殊的触发器,

在响应数据定义语言(DDL)时触发。

4.创建触发器的 Transact-SQL 语句 CREATE TRIGGER <触发器名>

ON <表名 | 视图名 >

[WITH ENCRYPTION]

<FOR | AFTER | INSTEAD OF> < [ INSERT ] [ , ] [ UPDATE ] [ , ] [DELETE]>

AS

[IF UPDATE (列名)]

<SQL 语句 [ ...n ]>

其中:

WITH ENCRYPTION 选项加密触发器代码,保护作者的知识产权。

UPDATE (列名)是一个函数,用于测试是否对表或视图的指定列进行了 INSERT 或 UPDATE 操作。

触发器可以响应更新、插入与删除。可以对两个或三个这类操作建立相同的触发器。例 如,可以对删除和更新生成触发器。

在 SQL Server 2005 中,FOR 和 AFTER 的作用相同,FOR 是为了保持与以前版本兼容,

建议使用 AFTER。SQL Server 7.0 不支持 AFTER,只能使用 FOR。

触发器与表相关联。如果删除表,则与这个表相关联的所有触发器都将被删除。

7.4.2 AFTER 触发器

AFTER 触发器是在执行 INSERT、UPDATE、DELETE 任一操作之后被触发。AFTER 触 发器只能在表上定义。可以针对表的同一操作定义多个触发器,也可以针对多个操作定义同一 触发器。

1.INSERT 触发器

INSERT 触发器由 INSERT 语句触发,即用户在表中插入一条记录且插入成功时,触发 INSERT 触发器。

例 7-18 创建一触发器以实现如下功能:当往 BORROW 表中插入一条记录时,如果书 号或卡号不存在,则撤消插入。

USE BookSys --使用 BookSys 数据库

/*判断是否存在触发器 t_BORROW1,存在则删除*/

IF EXISTS(SELECT NAME FROM SYSOBJECTS WHERE NAME='t_BORROW1') DROP TRIGGER t_BORROW1

GO

CREATE TRIGGER t_BORROW1 --创建触发器 ON BORROW

AFTER INSERT --INSERT(插入)触发器

AS

DECLARE @BOOKID CHAR(20) --声明一个变量以存储书号 DECLARE @CARDID CHAR(10) --声明一个变量以存储卡号

/*从临时表中检索出新记录的书号和卡号*/

SELECT @BOOKID=BOOKID,@CARDID=CARDID FROM INSERTED /*如果书号在其父表中不存在或者卡号在其父表中不存在*/

IF (NOT EXISTS(SELECT * FROM BOOK WHERE BOOKID=@BOOKID)) OR (NOT EXISTS(SELECT * FROM READER WHERE CARDID=@CARDID)) BEGIN

ROLLBACK TRANSACTION --撤消插入到表中的记录 RAISERROR('书号或卡号不存在!',16,1)

END

INSERT 触发器的工作过程如下:

(1)用户或系统运行 INSERT 语句。

(2)如果记录不违反限制,则将记录插入到临时表 INSERTED 中。

(3)触发触发器。

(4)如果触发器执行完毕而无错误,则 INSERTED 表被删除,插入操作完成。

注 意 : 如 果 在 触 发 器 中 检 测 到 插 入 的 数 据 不 符 合 要 求 , 可 以 用 ROLLBACK TRANSACTION 语 句 取 消 插 入 操 作 。 如 果 触 发 器 出 现 异 常 , 但 没 有 执 行 ROLLBACK TRANSACTION 语句,已插入的数据不会回滚。

2.DELETE 触发器

DELETE 触发器的过程与 INSERT 触发器相似,只是使用的是 DELETED 表,其中包含刚 刚删除的记录,而不是 INSERTED 表。

例 7-19 创建一触发器以实现如下功能:当试图删除 BORROW 表中的一条记录时,若 还书日期为空或还书日期距今还不到半年,则撤消事务。我们基于这样的假设:如果某书尚未 归还,显然不能删除;另外,我们希望将读者的还书记录保留半年之久。

USE BookSys --使用 BookSys 数据库

/*判断是否存在触发器 t_BORROW2,存在则删除*/

IF EXISTS(SELECT NAME FROM SYSOBJECTS WHERE NAME='t_BORROW2') DROP TRIGGER t_BORROW2

GO

CREATE TRIGGER t_BORROW2 --创建触发器 ON BORROW

AFTER DELETE --DELETE(删除)触发器

AS

DECLARE @SDATE SMALLDATETIME --声明一个变量以存储还书日期 /*从临时表中检索出被删除记录的还书日期至变量中*/

SELECT @SDATE=SDATE FROM DELETED /*如果还书日期为空或者还不到半年*/

IF (@SDATE IS NULL) OR DATEDIFF(MM,@SDATE,GETDATE())>6 BEGIN

RAISERROR('不允许删除这条记录,因为读者尚未还书,或还书还不到半年',16,1) ROLLBACK TRANSACTION --撤消从表中删除的记录

END

DELETE 触发器的工作过程如下:

(1)用户或系统运行 DELETE 语句。

( 2) 如 果 记 录 不 违 反外 部 关 键 字 限制 , 则 删 除表 中 的 记 录 并 将 其 插入 到 临 时表 DELETED 中。

(3)触发触发器。

(4)如果触发器执行完毕而无错误,则 DELETED 表被删除,删除操作完成。

3.UPDATE 触发器。

更新操作可以看成一个删除加一个插入:删除旧值和插入新值。

例 7-20 创建一触发器以实现如下功能:如果要更改 READER 表中的 CARDID,则先检

查 BORROW 表中是否有记录引用了该 CARDID。如果有引用,则不能更改;如果没有引用,

则可以更改。

USE BookSys --使用 BookSys 数据库 /*判断是否存在触发器 t_READER1,存在则删除*/

IF EXISTS(SELECT NAME FROM SYSOBJECTS WHERE NAME='t_READER1') DROP TRIGGER t_READER1

GO

CREATE TRIGGER t_READER1 ON READER

AFTER UPDATE AS

IF UPDATE(CARDID) ----如果 CARDID 列被修改 BEGIN

/*如果 BORROW 表中存在该 CARDID*/

IF EXISTS(SELECT BORROW.CARDID FROM BORROW, DELETED WHERE BORROW.CARDID=DELETED.CARDID)

BEGIN

ROLLBACK TRANSACTION

RAISERROR('该卡号正在使用,不能更改',16,1) END

END

UPDATE 触发器的工作过程如下:

(1)用户或系统运行 UPDATE 语句。

(2)如果记录不违反限制,则更新表并把旧记录插入到 DELETED 表中,把新记录插入 到 INSERTED 表中。

(3)触发触发器。

(4)如果触发器执行完毕而无错误,则 DELETED 表和 INSERTED 表被删除,更新操作 完成。

7.4.3 INSTEAD OF 触发器

SQL Server 2005 开始引入了 INSTEAD OF 触发器。AFTER 触发器的一个主要缺点是发生 在触发的语句执行之后。从前面的例子中可以看出,如果触发器出现异常或更新操作(插入、

更新、删除)不满足触发器中定义的规则,则要撤消事务。

INSTEAD OF 触 发 器 可 以 对 表 或 视 图 生 成 , 但 表 或 视 图 中 的 每 个 操 作 只 能 有 一 个 INSTEAD OF 触发器。

对于视图,INSTEAD OF 触发器改进视图的可更新性。我们看到,视图只能一次更新、

插入或删除一个基表数据。使用 INSTEAD OF 触发器则可以克服这个限制。但 INSTEAD OF 触 发器不可以用于使用 WITH CHECK OPTION 的可更新视图。

1.INSTEAD OF INSERT 触发器

与 AFTER 触发器一样,INSTEAD OF INSERT 触发器也使用 INSERTED 表,但逻辑稍有 不同。

(1)用户或系统运行 INSERT 语句。

(2)记录只插入到 INSERTED 表中。

(3)如果记录不违反任何限制,则触发器负责将记录实际地插入到数据表中,否则不插 入到表中。

注意:第一,记录只插入 INSERTED 表中而不插入基表中。因此,如果触发器中的任何 测试失败,什么也不用撤消。第二,表中并没有真正发生插入。触发器可以在测试数值之后决 定插入记录,但如果触发器代码进行其他操作,则并不进行插入。触发器执行,而基础 INSERT 语句并不执行。

例 7-21 下面的这个触发器用 INSTEAD OF 触发器替代了例 7-17 的 AFTER 触发器。

USE BookSys --使用 BookSys 数据库

/*判断是否存在触发器 t_BORROW3,存在则删除*/

CREATE TRIGGER t_BORROW3 --创建触发器 ON BORROW

INSTEAD OF INSERT AS

DECLARE @BOOKID CHAR(20) DECLARE @CARDID CHAR(10)

SELECT @BOOKID=BOOKID,@CARDID=CARDID FROM INSERTED /*如果 CARDID 和 BOOKID 在各自的父表中都存在*/

IF (EXISTS(SELECT * FROM BOOK WHERE BOOKID=@BOOKID)) AND (EXISTS(SELECT * FROM READER WHERE CARDID=@CARDID))

/*由触发器负责插入新记录*/

INSERT INTO BORROW SELECT * FROM INSERTED ELSE

RAISERROR('书号或卡号不存在!',16,1)

从中可以看出它的一大优点:如果不存在卡号或书号,不需要撤消事务。

2.INSTEAD OF DELETE 触发器

INSTEAD OF DELETE 触发器和 INSTEAD OF INSERT 触发器相似,但使用 DELETED 表。

(1)用户或系统运行 DELETE 语句。

(2)要删除记录的一个副本拷贝到 DELETED 表中。

(3)如果记录不违反任何限制,则触发器负责将记录从表中删除,否则不删除。

和 INSTEAD OF INSERT 触发器相似,除了触发器的操作以外,表格不进行任何操作。

注意:FOREIGN KEY 限制定义了 ON DELETE CASCASE 选项的表不能定义 INSTEAD OF DELETE 触发器。

例 7-22 下面的这个触发器用 INSTEAD OF DELETE 触发器替代了例 7-18 的 AFTER 触 发器。

USE BookSys --使用 BookSys 数据库

/*判断是否存在触发器 t_BORROW4,存在则删除*/

IF EXISTS(SELECT NAME FROM SYSOBJECTS WHERE NAME='t_BORROW4') DROP TRIGGER t_BORROW4

GO

CREATE TRIGGER t_BORROW4 --创建触发器 ON BORROW

INSTEAD OF DELETE AS

DECLARE @BOOKID CHAR(20) DECLARE @CARDID CHAR(10) DECLARE @SDATE SMALLDATETIME

SELECT @BOOKID=BOOKID,@CARDID=CARDID,

@SDATE=SDATE FROM DELETED /*如果还书日期已超过半年*/

IF DATEDIFF(MM,@SDATE,GETDATE())>6 /*由触发器负责删除该条记录*/

DELETE FROM BORROW

WHERE BOOKID=@BOOKID AND CARDID=@CARDID ELSE

RAISERROR('不允许删除这条记录,因为读者尚未还书,或还书尚不到半年',16,1) 3.INSTEEAD OF UPDATE 触发器

INSTEAD OF UPDATE 触发器用 DELETED 表和 INSERTED 表存储更新前后的记录。基

INSTEAD OF UPDATE 触发器用 DELETED 表和 INSERTED 表存储更新前后的记录。基

相關文件