触发器实际上就是一种特殊类型的存储过程,其特殊性表如今:它是在执行某些特定的T-SQL语句时自动的。前端
11.1 触发器简介sql
触发器实际上就是一种特殊类型的存储过程,它是在执行某些特定的T-SQL语句时自动执行的一种存储过程。在SQL Server 2005中,根据SQL语句的不一样,把触发器分为两类:一类是DML触发器,一类是DLL触发器。数据库
11.1.1 触发器的概念和做用编程
在SQL Server 2005里,能够用两种方法来保证数据的有效性和完整性:约束(check)和触发器(Trigger)。约束是直接设置于数据表内,只能现实一些比较简 单的功能操做,如:实现字段有效性和惟一性的检查、自动填入默认值、确保字段数据不重复(即主键)、确保数据表对应的完整性(即外键)等功能。服务器
触发器是针对数据表(库)的特殊的存储过程,当这个表发生了 Insert、Update或Delete操做时,会自动激活执行的,能够处理各类复杂的操做。在SQL Server 2005中,触发器有了更进一步的功能,在数据表(库)发生Create、Alter和Drop操做时,也会自动激活执行。架构
触发器经常使用的一些功能以下:编辑器
l 完成比约束更复杂的数据约束:触发器能够实现比约束更为复杂的数据约束ide
l 检查所作的SQL是否容许:触发器能够检查SQL所作的操做是否被容许。例如:在产品库存表里,若是要删除一条产品记录,在删除记录时,触发器能够检查该产品库存数量是否为零,若是不为零则取消该删除操做。函数
l 修改其它数据表里的数据:当一个SQL语句对数据表进行操做的时候,触发器能够根据该SQL语句的操做状况来对另外一个数据表进行操做。例如:一个订单取消的时候,那么触发器能够自动修改产品库存表,在订购量的字段上减去被取消订单的订购数量。工具
l 调用更多的存储过程:约束的自己是不能调用存储过程的,可是触发器自己就是一种存储过程,而存储过程是能够嵌套使用的,因此触发器也能够调用一个或多过存储过程。
l 发送SQL Mail:在SQL语句执行完以后,触发器能够判断更改过的记录是否达到必定条件,若是达到这个条件的话,触发器能够自动调用SQL Mail来发送邮件。例如:当一个订单交费以后,能够物流人员发送Email,通知他尽快发货。
l 返回自定义的错误信息:约束是不能返回信息的,而触发器能够。例如插入一条重复记录时,能够返回一个具体的友好的错误信息给前台应用程序。
l 更改本来要操做的SQL语句:触发器能够修改本来要操做的SQL语句,例如本来的SQL语句是要删除数据表里的记录,但该数据表里的记录是最要记录,不容许删除的,那么触发器能够不执行该语句。
l 防止数据表构结更改或数据表被删除:为了保护已经建好的数据表,触发器能够在接收到Drop和Alter开头的SQL语句里,不进行对数据表的操做。
11.1.2 触发器的种类
在SQL Server 2005中,触发器能够分为两大类:DML触发器和DDL触发器
l DML触发器:DML触发器是当数据库服务器中发生数据操做语言(Data Manipulation Language)事件时执行的存储过程。DML触发器又分为两类:After触发器和Instead Of触发器
l DDL触发器:DDL触发器是在响应数据定义语言(Data Definition Language)事件时执行的存储过程。DDL触发器通常用于执行数据库中管理任务。如审核和规范数据库操做、防止数据库表结构被修改等。
11.2 DML触发器的分类
SQL Server 2005的DML触发器分为两类:
l After触发器:这类触发器是在记录已经改变完以后(after),才会被激活执行,它主要是用于记录变动后的处理或检查,一旦发现错误,也能够用Rollback Transaction语句来回滚本次的操做。
l Instead Of触发器:这类触发器通常是用来取代本来的操做,在记录变动以前发生的,它并不去执行原来SQL语句里的操做(Insert、Update、Delete),而去执行触发器自己所定义的操做。
11.3 DML触发器的工做原理
在SQL Server 2005里,为每一个DML触发器都定义了两个特殊的表,一个是插入表,一个是删除表。这两个表是建在数据库服务器的内存中的,是由系统管理的逻辑表,而不是真正存储在数据库中的物理表。对于这两个表,用户只有读取的权限,没有修改的权限。
这两个表的结构与触发器所在数据表的结构是彻底一致的,当触发器的工做完成以后,这两个表也将会从内存中删除。
插入表里存放的是更新前的记录:对于插入记录操做来讲,插入表里存放的是要插入的数据;对于更新记录操做来讲,插入表里存放的是要更新的记录。
删除表里存放的是更新后的记录:对于更新记录操做来讲,删除表里存放的是更新前的记录(更新完后即被删除);对于删除记录操做来讲,删除表里存入的是被删除的旧记录。
下面看一下触发器的工做原理。
11.3.1 After触发器的工做原理
After触发器是在记录更变完以后才被激活执行的。以删除记录为 例:当SQL Server接收到一个要执行删除操做的SQL语句时,SQL Server先将要删除的记录存放在删除表里,而后把数据表里的记录删除,再激活After触发器,执行After触发器里的SQL语句。执行完毕以后, 删除内存中的删除表,退出整个操做。
仍是举上面的例子:在产品库存表里,若是要删除一条产品记录,在删除记录时,触发器能够检查该产品库存数量是否为零,若是不为零则取消删除操做。看一下数据库是怎么操做的:
(1)接收SQL语句,将要从产品库存表里删除的产品记录取出来,放在删除表里。
(2)从产品库存表里删除该产品记录。
(3)从删除表里读出该产品的库存数量字段,判断是否是为零,若是为零的话,完成操做,从内存里清除删除表;若是不为零的话,用Rollback Transaction语句来回滚操做。
11.3.2 Instead Of触发器的工做原理
Instead Of触发器与After触发器不一样。After触发器是在Insert、Update和Delete操做完成后才激活的,而Instead Of触发器,是在这些操做进行以前就激活了,而且再也不去执行原来的SQL操做,而去运行触发器自己的SQL语句。
11.4 设计DML触发器的注意事项及技巧
在了解触发器的种类和工做理由以后,如今能够开始动手来设计触发器了,不过在动手以前,还有一些注意事项必须先了解一下:
11.4.1 设计触发器的限制
在触发器中,有一些SQL语句是不能使用的,这些语句包括:
表11.1 在DML触发器中不能使用的语句
不能使用的语句 |
语句功能 |
Alter Database |
修改数据库 |
Create Database |
新建数据库 |
Drop Database |
删除数据库 |
Load Database |
导入数据库 |
Load Log |
导入日志 |
Reconfigure |
更新配置选项 |
Restore Database |
还原数据库 |
Restore Log |
还原数据库日志 |
另外,在对做为触发操做的目标的表或视图使用了下面的SQL语句时,不容许在DML触发器里再使用这些语句:
表11.2 在目标表中使用过的,DML触发器不能再使用的语句
不能使用的语句 |
语句功能 |
Create Index |
创建索引 |
Alter Index |
修改索引 |
Drop Index |
删除索引 |
DBCC Dbreindex |
从新生成索引 |
Alter Partition Function |
经过拆分或合并边界值更改分区 |
Drop Table |
删除数据表 |
Alter Table |
修改数据表结构 |
11.4.2 如何在触发器取得字段修改前和修改后的数据
上面介绍过,SQL Server 2005在为每一个触发器都定义了两个虚拟表,一个是插入表(inserted),一个是删除表(deleted),如今把这两个表存放的数据列表说明一下:
表11.3 插入/删除表的功能
激活触发器的动做 |
Inserted表 |
Deleted表 |
Insert |
存放要插入的记录 |
|
Update |
存放要更新的记录 |
存放更新前的旧记录 |
Delete |
存放要删除的旧记录 |
以上面删除库存产品记录为例,在删除时触发器要判断库存数量是否为零,那么判断就应该这么写:
If (Select 库存数量 From Deleted)>0
Begin
Print ‘库存数量大于零时不能删除此记录’
Rollback Transaction
End
11.4.3 其余注意事项
l After触发器只能用于数据表中,Instead Of触发器能够用于数据表和视图上,但两种触发器都不能够创建在临时表上。
l 一个数据表能够有多个触发器,可是一个触发器只能对应一个表。
l 在同一个数据表中,对每一个操做(如Insert、Update、Delete)而言能够创建许多个After触发器,但Instead Of触发器针对每一个操做只有创建一个。
l 若是针对某个操做即设置了After触发器又设置了Instead Of触发器,那么Instead of触发器必定会激活,而After触发器就不必定会激活了。
l Truncate Table语句虽然相似于Delete语句能够删除记录,可是它不能激活Delete类型的触发器。由于Truncate Table语句是不记入日志的。
l WRITETEXT语句不能触发Insert和Update型的触发器。
l 不一样的SQL语句,能够触发同一个触发器,如Insert和Update语句均可以激活同一个触发器。
11.5 设计After触发器
在了解触发器及其种类、做用、工做原理以后,下面详细讲述一下要怎么去设计及创建触发器。
11.5.1 设计简单的After触发器
下面用实例设计一个简单的After Insert触发器,这个触发器的做用是:在插入一条记录的时候,发出“又添加了一种产品”的友好提示。
(1)启动Management Studio,登陆到指定的服务器上。
(2)在如图11.1所示界面的【对象资源管理器】下选择【数据库】,定位到【Northwind】数据库à【表】à【dbo.产品】,并找到【触发器】项。
图11.1 定位到触发器
(3)右击【触发器】,在弹出的快捷菜单中选择【新建触发器】选项,此时会自动弹出【查询编辑器】对话框,在【查询编辑器】的编辑区里SQL Server已经预写入了一些创建触发器相关的SQL语句,如图11.2所示。
图11.2 SQL Server 2005预写的触发器代码
(4)修改【查询编辑器】里的代码,将从“CREATE”开始到“GO”结束的代码改成如下代码:
CREATE TRIGGER 产品_Insert
ON 产品
AFTER INSERT
AS
BEGIN
print '又添加了一种产品'
END
GO
若是有兴趣的话,也能够去修改一下如图11.2中绿色部分的版权信息。
(5)单击工具栏中的【分析】按钮 ,检查一下是否语法有错,如图11.3所示,若是在下面的【结果】对话框中出现“命令已成功完成”,则表示语法没有错误。
图11.3 检查语法
(6)语法检查无误后,单击【执行】按钮,生成触发器。
(7)关掉查询编辑器对话框,刷新一下触发器对话框,能够看到刚才创建的【产品_Insert】触发器,如图11.4所示。
图11.4 建好的触发器
创建After Update触发器、After Delete触发器和创建After Insert触发器的步骤一致,不一样的地方是把上面的SQL语句中的AFTER INSERT分别改成AFTER UPDATE和AFTER DELETE便可,以下所示,有兴趣的读者能够自行测试。
CREATE TRIGGER 产品_Update
ON 产品
AFTER UPDATE
AS
BEGIN
print '有一种产品更改了'
END
GO
CREATE TRIGGER 产品_Delete
ON 产品
AFTER DELETE
AS
BEGIN
print '又删除了一种产品'
END
GO
11.5.2 测试触发器功能
建好After Insert触发器以后,如今来测试一下触发器是怎么样被激活的。
(1)在Management Studio里新建一个查询,在弹出的【查询编辑器】对话框里输入如下代码:
INSERT INTO 产品(产品名称) VALUES ('大苹果')
(2)单击【执行】按钮,能够看到【消息】对话框里显示出一句提示:“又添加了一种产品”,如图11.5所示,这说明,After Insert触发器被激活,并运行成功了。
图11.5 查看触发器的运行结果
而若是在【查询编辑器】里执行的不是一个Insert语句,而是一个Delete语句的话,After Insert触发器将不会被激活。如在【查询编辑器】输入如下语句:
DELETE FROM 产品 WHERE (产品名称= '大苹果')
单击【执行】按钮,在【消息】对话框里只显示了一句“(1行受影 响)”的提示,而没有“又添加了一种产品”的提示,如图11.6所示。这是由于Delete语句是不能激活After Insert触发器,因此After Insert触发器里的“print ‘又添加了一种产品’”语句并无执行。
图11.6 执行删除语句不会激活After Insert触发器
11.5.3 创建触发器的SQL语句
回顾一下,在Management Studio新建一个触发器的时候,它在查询分析对话框给预设了一些SQL代码,这些代码其实上就是创建触发器的语法提示。如今来看一下完整的触发器语法代码:
CREATE TRIGGER <Schema_Name, sysname, Schema_Name>.<Trigger_Name, sysname, Trigger_Name>
ON <Schema_Name, sysname, Schema_Name>.<Table_Name, sysname, Table_Name>
AFTER <Data_Modification_Statements, , INSERT,DELETE,UPDATE>
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for trigger here
END
GO
用中文改了一下,以上代码就一目了然了:
CREATE TRIGGER 触发器名
ON 数据表名或视图名
AFTER INSERT或DELETE或UPDATE
AS
BEGIN
--这里是要运行的SQL语句
END
GO
如今再对上面的代码进行进一步的说明:
l CREATE TRIGGER 触发器名:这一句声明SQL语句是用来创建一个触发器。其中触发器名在所在的数据库里必须是惟一的。因为触发器是创建中数据表或视图中的,因此有不少人都 觉得只要是在不一样的数据表中,触发器的名称就能够相同,其实触发器的全名(Server.Database.Owner.TriggerName)是必须 惟一的,这与触发器在哪一个数据表或视图无关。
l ON 数据表名或视图名:这是指定触发器所在的数据表或视图,可是请注意,只有Instead Of触发器才能创建在视图上。而且,有设置为With Check Option的视图也不容许创建Instead Of触发器。
l AFTER INSERT或 DELETE UPDATE:这是指定触发器的类型,是After Insert触发器,仍是After Delete触发器,或者是After Update触发器。其中After能够用For来代取,它们的意思都是同样的,表明只有在数据表的操做都已正确完成后才会激活的触发器。INSERT、 DELETE和UPDATE至少要指定一个,固然也能够指定多个,若指定多个时,必须用逗号来分开。其顺序能够任意摆放。
l With Encryption:With Encryption是用来加密触发器的,放在“On 数据表名或视图名”的后面,“For”的前面。若是使用了这句话,该触发器将会被加密,任何人都看不到触发器的内容了。
例一:如下是一个包含提醒电子邮件的触发器例子,若是订单表里记录有改动的的话(不管增长订单仍是修改、删除订单),则给物流人员张三发送电子邮件:
CREATE TRIGGER 订单_Insert
ON 订单
AFTER INSERT, UPDATE, DELETE
AS
EXEC master..xp_sendmail '张三',
'订单有更改,请查询肯定'
GO
例二:在订单明细表里,折扣字段不能大于0.6,若是插入记录时,折扣大于0.6的话,回滚操做。
CREATE TRIGGER 订单明细_Insert
ON 订单明细
AFTER INSERT
AS
BEGIN
if (Select 折扣 from inserted)>0.6
begin
print '折扣不能大于0.6'
Rollback Transaction
end
END
GO
在示例二中运用了两个方法,一个是前面说过的,在Inserted表里查询某个字段,还有一个是用Rollback Transaction来回滚操做。若是用下面的SQL语句来进行Insert操做的话,插入记录将会不成功。
INSERT INTO 订单明细(订单ID,产品ID,单价,数量,折扣)
VALUES (11077,1,18,1,0.7)
运行结果如图11.7所示:
图11.7 插入记录不符合触发器里的约束,则回滚操做
11.6 设置After触发器的激活顺序
对于同一个操做,如Insert、Update或Delete来讲,能够创建多个After Insert触发器,在11.5.1节中,已经创建了一个名为“产品_Insert”的触发器,如今再创建一个After Insert触发器,做用也是输出一句有好提示,提示内容为:“再一次告诉你,你又添加了一种产品”。
CREATE TRIGGER 产品_Insert1
ON 产品
AFTER INSERT
AS
BEGIN
print '再一次告诉你,你又添加了一种产品'
END
GO
从新运行一下插入产品的SQL语句:
INSERT INTO 产品(产品名称)
VALUES ('大苹果')
如图11.8所示,运行一个Insert语句,在【消息】能够看到一共输出了两句话,说明激活两个不一样的触发器。
图11.8 一个语句激活两个触发器
当同一个操做定义的触发器愈来愈多的时候,触发器被激活的次序就会 变得愈来愈重要了。在SQL Server 2005里,用存储过程【sp_settriggerorder】能够为每个操做各指定一个最早执行的After触发器和最后执行的After触发器。 sp_settriggerorder语法以下:
sp_settriggerorder [ @triggername = ] '[ triggerschema. ] triggername'
, [ @order = ] 'value'
, [ @stmttype = ] 'statement_type'
[ , [ @namespace = ] { 'DATABASE' | 'SERVER' | NULL } ]
翻译成中文就是
sp_settriggerorder 触发器名,
激活次序,
激活触发器的动做
解释以下:
l 触发器名,要用单引号括起来,由于它是一个字符串。
l 激活次序能够为First、Last和None:First是指第一个要激活的触发器;Last是指它最后一个要激活的触发器;None是不指激活序,由程序任意触发。
l 激活触发器的动做能够是:Insert、Update和Delete。
上面的例子里,先激活的是【产品_Insert】触发器,后激活的是【产品_Insert1】触发器。若是把【产品_Insert1】触发器设为First触发器,把【产品_Insert】触发器设为Last触发器,那么结果将会彻底不同。设置语句以下:
Exec sp_settriggerorder
'产品_Insert1','First','Insert'
go
Exec sp_settriggerorder
'产品_Insert',’Last’,'Insert'
Go
从新运行一下插入产品的SQL语句:
INSERT INTO 产品(产品名称)
VALUES ('大苹果')
运行结果如图11.9,与图11.8比较一下,是否是激活次序已经发生变化了?
图11.9 按次序激活的激活器
在设置After触发器激活顺序时,还有几点是须要注意的:
l 每一个操做最多只能设一个First触发器和一个Last触发器。
l 若是要取消已经设好的First触发器或Last触发器,只要把它们设为None触发器便可。
l 若是用Alter命令修改过触发器内容后,该触发器会自动变成None触发器。因此用Alter命令也能够用来取消已经设好的First触发器或Last触发器。
l 只有After触发器能够设置激活次序,Instead Of触发器不能够设置激活次序。
l 激活触发器的动做必须和触发器内部的激活动做一致。举例说明:After Insert触发器,只能为Insert操做设置激活次序,不能为Delete操做设置激活次序。如下的设置是错误的:
Exec sp_settriggerorder
'产品_Insert1','First',’Update’
go
11.7 触发器的嵌套
当一个触发器执行时,可以触活另外一个触发器,这种状况就是触发器的嵌套。在SQL Server 2005里,触发器可以嵌套到32层。
若是不想对触发器进行嵌套的话,能够经过【容许触发器激活其余触发器】的服务器配置选项来控制。但无论此设置是什么,均可以嵌套Instead Of触发器。设置触发器嵌套的选项更改方法为:
(1)打开Management Studio,在【对象资源管理】中,右击服务器名,并选择【属性】选项。
(2)单击【高级】节点。
(3)在【杂项】里设置【容许触发器激活其余触发器】为True或False。如图11.10所示:
图11.10 开启/关闭触发器嵌套
如今,在Northwind数据库里建一个操做记录表,用来记录全部数据表的操做,不管是对哪一个数据表进行了插入、更新或删除,均可以把操做内容和操做时间记录到操做记录表里。下面是创建操做记录表的SQL语句:
CREATE TABLE 操做记录表(
编号 int IDENTITY(1,1) NOT NULL,
操做表名 varchar(50) NOT NULL,
操做语句 varchar(2000) NOT NULL,
操做内容 varchar(2000) NOT NULL,
操做时间 datetime NOT NULL
CONSTRAINT DF_操做记录表_操做时间 DEFAULT (getdate()),
CONSTRAINT PK_操做记录表 PRIMARY KEY CLUSTERED
(
编号 ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
为了简便起见,在操做记录表里,只建一个After Insert触发器,触发器的做用是输入一条语句:“数据库又有记录变更了”。在实际应用时,读者可自行修改为所需的代码。
CREATE TRIGGER 操做记录表_Insert
ON 操做记录表
AFTER INSERT
AS
BEGIN
print '数据库又有记录变更了'
END
GO
做为示例,只在类别表里创建一个After Insert触发器,当在类别表里插入一条记录的时候,该触发器向操做记录表里插入一条记录,而在操做记录表里插入记录时,将会触发操做记录表里的【操做记录表_Insert】触发器。
CREATE TRIGGER 类别_Insert
ON 类别
AFTER INSERT
AS
BEGIN
Declare
@类别名称 nvarchar(15),
@说明 nvarchar(max)
set @类别名称= (Select 类别名称 from inserted)
set @说明= (Select 说明 from inserted)
INSERT INTO 操做记录表(操做表名,操做语句,操做内容)
VALUES ('类别表','插入记录','类别名称:'+@类别名称+',说明:'+@说明)
END
GO
如今运行一下对类别表的插入语句
INSERT INTO 类别(类别名称,说明)
VALUES ('书籍','各种图书')
运行结果如图11.11所示:
图11.11 触发器嵌套被激活
在【消息】对话框能够看到“数据库又有记录变更了”,这说明,触发器已经被嵌套激活了。若是把【容许触发器激活其余触发器】的选项设为False,再看看运行结果:
图11.12 触发器嵌套没有被激活
如图11.12所示,如今没有“数据库又有记录变更了”的提示输出,说明嵌套的触发器没有被激活。
11.8 触发器的递归
触发器的递归是指,一个触发器从其内部又一次激活该触发器。例如一 个Insert触发器的内部还有一条对本数据表插入记录的SQL语句,那么这个插入语句就有可能再一次激活这个触发器自己。固然,这种递归的触发器内部还 会有判断语句,要必定的状况下才会执行那个SQL语句,不然的话,就会变成死循环了。
上面的例子说的是直接递归的触发器,还有一种是间接递归的触发器, 举例说明:当向A表插入一条记录时,激活了A表的Insert触发器,A表的Insert触发器里有一个SQL语句是对B表进行Insert操做的,而在 B表的Insert触发器里也有一句话是对A表进行Insert操做的。这样就是触发器的间接递归。
通常状况来讲,SQL Server服务器是不容许递归的,若是要打开触发器递归的功能,一样是将【容许触发器激活其余触发器】设为True,如图11.10所示。
11.9 设计Instead Of触发器
Instead Of触发器与After触发器的工做流程是不同的。After触发器是在SQL Server服务器接到执行SQL语句请求以后,先创建临时的Inserted表和Deleted表,而后实际更改数据,最后才激活触发器的。而 Instead Of触发器看起来就简单多了,在SQL Server服务器接到执行SQL语句请求后,先创建临时的Inserted表和Deleted表,而后就触发了Instead Of触发器,至于那个SQL语句是插入数据、更新数据仍是删除数据,就一律无论了,把执行权全权交给了Instead Of触发器,由它去完成以后的操做。
11.9.1 Instead Of触发器的使用范围
Instead Of触发器能够同时在数据表和视图中使用,一般在如下几种状况下,建议使用Instead Of触发器:
l 数据库里的数据禁止修改:例如电信部门的通话记录是不能修改的,一旦修改,则通话费用的计数将不正确。在这个时候,就能够用Instead Of触发器来跳过Update修改记录的SQL语句。
l 有可能要回滚修改的SQL语句:如11.5.3节中的例二,用After触发器并非一个最好的方法,若是用Instead Of触发器,在判断折扣大于0.6时,就停止了更新操做,避免在修改数据以后再回滚操做,减小服务器负担。
l 在视图中使用触发器:由于After触发器不能在视图中使用,若是想在视图中使用触发器,就只能用Instead Of触发器。
l 用本身的方式去修改数据:如不满意SQL直接的修改数据的方式,可用Instead Of触发器来控制数据的修改方式和流程。
11.9.2 设计简单的Instead Of触发器
Instead Of触发器的语法以下:
CREATE TRIGGER 触发器名
ON 数据表名或视图名
Instead Of INSERT或DELETE或UPDATE
AS
BEGIN
--这里是要运行的SQL语句
END
GO
从上面能够看得出,Instead Of触发器与After触发器的语法几乎一致,只是简单地把After改成Instead Of。前面说过的11.5.3节中的例二,用After触发器并非一个最好的方法,若是用Instead Of触发器,在判断折扣大于0.6时,就停止了更新操做,避免在修改数据以后再回滚操做,减小服务器负担。现将原来的触发器改成Instead Of触发器:
CREATE TRIGGER 订单明细_Insert
ON 订单明细
Instead Of INSERT
AS
BEGIN
SET NOCOUNT ON;
declare
@订单ID int,
@产品ID int,
@单价 money,
@数量 smallint,
@折扣 real
set @订单ID = (select 订单ID from inserted)
set @产品ID = (select 产品ID from inserted)
set @单价 = (select 单价 from inserted)
set @数量 = (select 数量 from inserted)
set @折扣 = (select 折扣 from inserted)
if (@折扣)>0.6
print '折扣不能大
11.10 查看DML触发器
查看已经设计好的DML触发器有两种方式,一种是通用Management Studio来查看,一种是利用系统存储过程来查看。
11.10.1 在Management Studio中查看触发器
在Management Studio中查看触发器的步骤:
(1)启动Management Studio,登陆到指定的服务器上。
(2)在如图11.13所示界面的【对象资源管理器】下选择【数据库】,定位到要查看触发器的数据表上,并找到【触发器】项。
图11.13 查看触发器列表
(3)单击【触发器】,在右边的【摘要】对话框里,能够看到已经建 好的该数据表的触发器列表。若是在点击【触发器】后,右边没有显示【摘要】对话框,能够在单击菜单栏上的【视图】菜单,选择【摘要】选项,打开【摘要】对 话框。若是在【摘要】对话框里没有看到本应存在的触发器列表,能够【摘要】对话框里右击空白处,在弹出的快捷菜单中选择【刷新】选项,刷新对话框后便可看 到触发器列表。
(4)双击要查看的触发器名,Management Studio自动弹出一个【查询编辑器】对话框,对话框里显示的是该触发器的内容,如图11.14所示:
图11.14 查看触发器内容
11.10.2 用系统存储过程查看触发器
SQL Server 2005里已经建好了两个系统存储过程,能够用这两个系统存储过程来查看触发器的状况:
11.10.2.1 sp_help:
系统存储过程“sp_help”能够了解如触发器名称、类型、建立时间等基本信息,其语法格式为:
sp_help ‘触发器名’
举例:
sp_help '产品_Insert'
运行结果如图11.15所示,能够看到触发器“产品_insert”的基本状况。
图11.15 查看触发器的基本状况
11.10.2.2 sp_helptext:
系统存储过程“sp_helptext”能够查看触发器的文本信息,其语法格式为:
sp_helptext ‘触发器名’
举例:
sp_helptext '产品_Insert'
运行结果如图11.16所示,能够看到触发器“产品_insert”的具体文本内容。
图11.16 查看触发器的基本状况
于0.6'
else
INSERT INTO 订单明细
(订单ID,产品ID,单价,数量,折扣)
VALUES
(@订单ID,@产品ID,@单价,@数量,@折扣)
END
GO
上面的触发器里写入了一句“SET NOCOUNT ON”,这一句的做用是,屏蔽在触发器里Insert语句执行完以后返回的所影响行数的消息。
11.11 修改DML触发器
在Management Studio中修改触发器以前,必需要先查看触发器的内容,经过11.10.1节的第(1)步到第(4)步,细心的读者能够已经发现,如图11.14所 示,在【查询编辑器】对话框里显示的就是用来修改触发器的代码。编辑完代码以后,单击【执行】按钮运行便可。修改触发器的语法以下:
ALTER TRIGGER 触发器名
ON 数据表名或视图名
AFTER INSERT或DELETE或UPDATE
AS
BEGIN
--这里是要运行的SQL语句
END
GO
若是只要修改触发器的名称的话,也可使用存储过程“sp_rename”。其语法以下:
sp_rename ‘旧触发器名’,’新触发器名’
值得一提的是修改触发器名称有可能会使某些脚本或存储过程运行出错。
11.12 删除DML触发器
在Management Studio中删除触发器,必需要先查到触发器列表,经过11.10.1节的第(1)步到第(3)步,能够查看到数据表下的全部触发器列表,右击其中一个 触发器,在弹出快捷菜单中选择【删除】选项,此时将会弹出【删除对象】对话框,在该对话框中单击【肯定】按钮,删除操做完成。用如下SQL语句也对可删除 触发器:
Drop Trigger 触发器名
注意:若是一个数据表被删除,那么SQL Server会自动将与该表相关的触发器删除。
11.13 禁用与启用DML触发器
禁用触发器与删除触发器不一样,禁用触发器时,仍会为数据表定义该触发器,只是在执行Insert、Update或Delete语句时,除非从新启用触发器,不然不会执行触发器中的操做。
在Management Studio中禁用或启用触发器,也必需要先查到触发器列表,触发器列表里,右击其中一个触发器,在弹出快捷菜单中选择【禁用】选项,便可禁用该触发器。启用触发器与上相似,只是在弹出快捷菜单中选择【启用】选项便可。
用如下Alter Table语句也禁用或启用触发器,其语法以下:
Alter table 数据表名
Disable或Enable trigger 触发器名或ALL
用Disable能够禁用触发器,用Enable能够启用触发器;若是要禁用或启用全部触发器,用“ALL”来代替触发器名。
11.14 2005新增功能:DDL触发器
DDL触发器是SQL Server 2005新增的一个触发器类型,是一种特殊的触发器,它在响应数据定义语言(DDL)语句时触发。通常用于数据库中执行管理任务。
与DML触发器同样,DDL触发器也是经过事件来激活,并执行其中 的SQL语句的。但与DML触发器不一样,DML触发器是响应Insert、Update或Delete语句而激活的,DDL触发器是响应Create、 Alter或Drop开头的语句而激活的。通常来讲,在如下几种状况下可使用DDL触发器:
l 数据库里的库架构或数据表架构很重要,不容许被修改。
l 防止数据库或数据表被误操做删除。
l 在修改某个数据表结构的同时修改另外一个数据表的相应的结构。
l 要记录对数据库结构操做的事件。
11.15 2005新增功能:设计DDL触发器
只要注意到DDL触发器和DML触发器的区别,设计DDL触发器与设计DML触发器也很相似,下面详细讲述一下要怎么去设计一个DDL触发器。
11.15.1 创建DDL触发器的语句
创建DDL触发器的语法代码以下:
CREATE TRIGGER trigger_name
ON { ALL SERVER | DATABASE }
[ WITH <ddl_trigger_option> [ ,...n ] ]
{ FOR | AFTER } { event_type | event_group } [ ,...n ]
AS { sql_statement [ ; ] [ ...n ] | EXTERNAL NAME < method specifier > [ ; ] }
用中文取代一下英文能够看得更明白:
CREATE TRIGGER 触发器名
ON ALL SERVER或DATABASE
FOR 或AFTER
激活DDL触发器的事件
AS
要执行的SQL语句
其中:
l ON后面的All Server是将DDL触发器做用到整个当前的服务器上。若是指定了这个参数,在当前服务器上的任何一个数据库都能激活该触发器。
l ON后面的Database是将DDL触发器做用到当前数据库,只能在这个数据库上激活该触发器。
l For或After是同一个意思,指定的是After触发器,DDL触发器不能指定的Stead Of触发器。
l 激活DDL触发器的事件包括两种,在DDL触发器做用在当前数据库状况下可使用如下事件:
CREATE_APPLICATION_ROLE |
ALTER_APPLICATION_ROLE |
DROP_APPLICATION_ROLE |
CREATE_ASSEMBLY |
ALTER_ASSEMBLY |
DROP_ASSEMBLY |
ALTER_AUTHORIZATION _DATABASE |
||
CREATE_CERTIFICATE |
ALTER_CERTIFICATE |
DROP_CERTIFICATE |
CREATE_CONTRACT |
DROP_CONTRACT |
|
GRANT_DATABASE |
DENY_DATABASE |
REVOKE_DATABASE |
CREATE_EVENT_NOTIFICATION |
DROP_EVENT_NOTIFICATION |
|
CREATE_FUNCTION |
ALTER_FUNCTION |
DROP_FUNCTION |
CREATE_INDEX |
ALTER_INDEX |
DROP_INDEX |
CREATE_MESSAGE_TYPE |
ALTER_MESSAGE_TYPE |
DROP_MESSAGE_TYPE |
CREATE_PARTITION_FUNCTION |
ALTER_PARTITION_FUNCTION |
DROP_PARTITION_FUNCTION |
CREATE_PARTITION_SCHEME |
ALTER_PARTITION_SCHEME |
DROP_PARTITION_SCHEME |
CREATE_PROCEDURE |
ALTER_PROCEDURE |
DROP_PROCEDURE |
CREATE_QUEUE |
ALTER_QUEUE |
DROP_QUEUE |
CREATE_REMOTE_SERVICE _BINDING |
ALTER_REMOTE_SERVICE _BINDING |
DROP_REMOTE_SERVICE _BINDING |
CREATE_ROLE |
ALTER_ROLE |
DROP_ROLE |
CREATE_ROUTE |
ALTER_ROUTE |
DROP_ROUTE |
CREATE_SCHEMA |
ALTER_SCHEMA |
DROP_SCHEMA |
CREATE_SERVICE |
ALTER_SERVICE |
DROP_SERVICE |
CREATE_STATISTICS |
DROP_STATISTICS |
UPDATE_STATISTICS |
CREATE_SYNONYM |
DROP_SYNONYM |
CREATE_TABLE |
ALTER_TABLE |
DROP_TABLE |
|
CREATE_TRIGGER |
ALTER_TRIGGER |
DROP_TRIGGER |
CREATE_TYPE |
DROP_TYPE |
|
CREATE_USER |
ALTER_USER |
DROP_USER |
CREATE_VIEW |
ALTER_VIEW |
DROP_VIEW |
CREATE_XML_SCHEMA _COLLECTION |
ALTER_XML_SCHEMA _COLLECTION |
DROP_XML_SCHEMA _COLLECTION |
在DDL触发器做用在当前服务器状况下,可使用如下事件:
ALTER_AUTHORIZATION_SERVER |
||
CREATE_DATABASE |
ALTER_DATABASE |
DROP_DATABASE |
CREATE_ENDPOINT |
DROP_ENDPOINT |
|
CREATE_LOGIN |
ALTER_LOGIN |
DROP_LOGIN |
GRANT_SERVER |
DENY_SERVER |
REVOKE_SERVER |
例三,创建一个DDL触发器,用于保护数据库中的数据表不被修改,不被删除。具体操做步骤以下:
(1)启动Management Studio,登陆到指定的服务器上。
(2)在如图11.1所示界面的【对象资源管理器】下选择【数据库】,定位到【Northwind】数据库上。
(3)单击【新建查询】按钮,在弹出的【查询编辑器】的编辑区里输入如下代码:
CREATE TRIGGER 禁止对数据表操做
ON DATABASE
FOR DROP_TABLE, ALTER_TABLE
AS
PRINT '对不起,您不能对数据表进行操做'
ROLLBACK ;
(4)单击【执行】按钮,生成触发器。
例四,创建一个DDL触发器,用于保护当前SQL Server服务器里全部数据库不能被删除。具体代码以下:
CREATE TRIGGER 不容许删除数据库
ON all server
FOR DROP_DATABASE
AS
PRINT '对不起,您不能删除数据库'
ROLLBACK ;
GO
例五,创建一个DDL触发器,用来记录数据库修改状态。具体操做步骤以下:
(1)创建一个用于记录数据库修改状态的表:
CREATE TABLE 日志记录表(
编号 int IDENTITY(1,1) NOT NULL,
事件 varchar(5000) NULL,
所用语句 varchar(5000) NULL,
操做者 varchar(50) NULL,
发生时间 datetime NULL,
CONSTRAINT PK_日志记录表 PRIMARY KEY CLUSTERED
(
编号 ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
(2)创建DDL触发器:
CREATE TRIGGER 记录日志
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
AS
DECLARE @log XML
SET @log = EVENTDATA()
INSERT 日志记录表
(事件, 所用语句,操做者, 发生时间)
VALUES
(
@log.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)'),
@log.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(2000)'),
CONVERT(nvarchar(100), CURRENT_USER),
GETDATE()
) ;
GO
其中Eventdata是个数据库函数,它的做用是以XML格式返回有关服务器或数据库事件的信息。@log.value是返回log这个XML结点的值,结点的位置是括号里的第一个参数。
11.15.2 测试触发器功能
如今测试一下在上一章节中创建好的三个触发器的功能。下面全部的测试都是在【查询编辑器】对话框里进行的,要打开【查询编辑器】对话框,只要单击Management Studio里【新建查询】按钮便可。
测试例三:例三是保证【Northwind】数据库里不能删除表和修改表,在【查询编辑器】对话框里输入一个删除表的SQL语句:
Drop table 操做记录表
运行结果如图11.17所示:
图11.17 不容许删除表格
测试例四:例四是保证当前服务器里的全部数据库不能被删除,在【查询编辑器】对话框里输入一个删除数据库的SQL语句:
Drop DataBase test
运行结果如图11.18所示:
图11.18 不容许删除数据库
测试例五:例五是记录对【Northwind】所进行的操做,在【查询编辑器】对话框里输入一条添加数据表和一条删除数据表的SQL语句,而后再用Select语句查看【目志记录表】数据表里全部的记录:
CREATE TABLE 测试表(
编号int IDENTITY(1,1) NOT NULL,
测试内容varchar(50) NOT NULL)
GO
Drop table 测试表
GO
select * from 日志记录表
GO
运行时不要忘了,前面曾经创建过一个不能删除数据表的触发器,要先把它禁用或删除。运行结果如图11.19所示:
图11.19 记录对数据库的操做
11.16 2005新增功能:查看与修改DDL触发器
DDL触发器有两种,一种是做用在当前SQL Server服务器上的,一种是做用在当前数据库中的。这两种DDL触发器在Management Studio中所在的位置是不一样的。
l 做用在当前SQL Server服务器上的DDL触发器所在位置是:【对象资源管理器】,选择所在SQL Server服务器,定位到【服务器对象】à【触发器】,在【摘要】对话框里就能够看到全部的做用在当前SQL Server服务器上的DDL触发器。
l 做用在当前数据库中的DDL触发器所在位置是:【对象资源管理器】,选择所在SQL Server服务器,【数据库】,所在数据库,定位到【可编程性】à【数据库触发器】,在摘要对话框里就能够看到全部的当前数据库中的DDL触发器。
右击触发器,在弹出的快捷菜单中选择【编写数据库触发器脚本为】à【CREATE到】à【新查询编辑器对话框】,而后在新打开的【查询编辑器】对话框里能够看到该触发器的内容。
在Management Studio若是要修改DDL触发器内容,就只能先删除该触发器,再从新创建一个DDL触发器。
虽然在Management Studio中没有直接提供修改DDL触发器的对话框,但在【查询编辑器】对话框里依然能够用SQL语句来进行修改。下面给出几个对DDL触发器操做经常使用 的SQL代码,因为对DDL触发器的操做和对DML触发器的操做相似,所以再也不详细说明用法。
l 建立DDL触发器
CREATE TRIGGER (Transact-SQL)
l 删除DDL触发器
DROP TRIGGER (Transact-SQL)
l 修改DDL触发器
ALTER TRIGGER (Transact-SQL)
l 重命名DDL触发器
sp_rename (Transact-SQL)
l 禁用DDL触发器
DISABLE TRIGGER (Transact-SQL)
l 启用DDL触发器
ENABLE TRIGGER (Transact-SQL)
l 删除DDL触发器
DROP TRIGGER (Transact-SQL)
11.17 触发器的应用技巧
触发器的使用范围很广,使用的频率也很高,触发器的应用技巧也层出不穷,下面介绍一些在触发器里经常使用的技巧,但愿能够作到抛砖引玉之功效。
11.17.1 如何知道触发器修改了多少条记录
须要注意的是,一种操做类型(Insert、Update或Delete)虽然能够激活多个触发器,可是每一个操做类型在一次操做时,对一个触发器只激活一次。例如,运行一个Update语句,有可能一次更新了十条记录,可是对于After Update这个触发器,只激活一次,而不是十次。可是在Inserted表和Deleted表里会有十条记录,这个时候,只要利用@@Rowcount这个系统变量就能够得知更新了多少条记录。例如:
CREATE TRIGGER 订单明细删除_test
ON 订单明细
AFTER DELETE
AS
BEGIN
print '您这次删除了' + Cast(@@rowcount as varchar) + '条记录'
END
GO
Delete FROM 订单明细 where 折扣=0.25
GO
Delete FROM 订单明细 where 订单ID='123456789'
GO
这里先是创建了一个名为“订单明细删除_test”的触发器,做用就是显示删除了多少条记录。以后执行两个SQL语句,一个是删除折扣为0.25的记录,一个是删除订单ID号为123456789的记录,这条记录是不存在的。运行结果如图11.20所示:
图11.20 显示删除的记录数
在图11.20能够看出,用系统变量@@rowcount能够得到删除记录的条数。另外,在图中还能够看出,虽然第二个SQL语句删除的记录数为零,可是触发器仍是被激活了。所以能够知道,触发器只与激活它的类型有关,与具体操做的记录数无关。
11.17.2 如何知道插入记录的自动编号是多少
在第11.7节,触发器的嵌套里,【类别】数据表设计了一个触发 器,当在【类别】数据表里插入一件记录的时候,将会在【操做记录表】里也插入一条记录,用来记录具体的插入操做的,其实这个触发器还能够写得更好,不但可 以记录插入操做所用的SQL语句,还能够记录下当时插入记录时候,数据库为这个记录自动生成编号是多少,为之后的操做提供更大的便利。修改该触发器的代码 以下:
ALTER TRIGGER 类别_Insert
ON 类别
AFTER INSERT
AS
BEGIN
Declare
@类别名称 nvarchar(15),
@说明 nvarchar(max)
set @类别名称 = (Select 类别名称 from inserted)
set @说明 = (Select 说明 from inserted)
INSERT INTO 操做记录表 (操做表名,操做语句,操做内容)
VALUES ('类别表','插入记录',
'插入了ID号为'+cast(@@IDENTITY as varchar)+'的记录:类别名称:'
+@类别名称+',说明:'+@说明)
END
GO
从上面的代码能够看出,用@@IDENTITY能够得到刚插入记录的标识值,在本例中是它的主键值。插入记录后,在【操做记录表】里能够详细查看到插入的记录的编号以及它的内容。
11.17.3 如何知道某个字段是否被修改
在Update触发器和Insert触发器里,能够用“Update(字段名)”来判断某个字段是否是被更改,返回的是一个布尔值。例如定单生成后,只能修改折扣的触发器:
CREATE TRIGGER 只容许修改折扣
ON 订单明细
Instead Of UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(折扣)
begin
declare
@订单ID int,
@产品ID int,
@折扣 real
set @订单ID = (select 订单ID from inserted)
set @产品ID = (select 产品ID from inserted)
set @折扣 = (select 折扣 from inserted)
update 订单明细 set 折扣=@折扣
where 订单ID=@订单ID and 产品ID=@产品ID
end
else
begin
print '只能更改折扣字段'
end
END
GO
update 订单明细 set 折扣=0.2
where 订单ID=10288 and 产品ID=54
Go
update 订单明细 set 订单ID=10288
where 订单ID=10288 and 产品ID=54
Go
上面的代码,先创建了一个触发器,只有修改了折扣字段的Update语句才会被执行。而后写了两个Update的SQL语句,一个是修改了折扣字段的,一个是没有修改折扣字段的。运行后的结果如图11.21所示。第一个SQL语句被正确执行,第二个SQL语句没有被执行。
图11.21 用Update判断字段是否被修改
11.17.4 如何返回错误信息
虽然上面介绍触发器时,用过不少次Print来输出自定义的信息,可是实际上,只有在用【查询编辑器】中运行SQL语句才能看获得这些自定义的信息,而其余的前端应用程序都不会显示出这些自定义的信息,包括用Management Studio也同样。
读者能够自行测试一下,在Management Studio里打开【订单明细】数据表,由于上面建了一个【只容许修改折扣】的触发器,因此只要在不是折扣的字段里修改数据后,再将鼠标聚焦到其余记录上 时,被修改的数据立刻就会回滚到修改前的状态,在这个过程当中,几乎是看不到什么提示的。若是想要在这个过程当中看到提示的话,就要将触发器修改一下,加上 “Raiserror”语句,具体修改代码以下:
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go
ALTER TRIGGER 只容许修改折扣
ON 订单明细
Instead Of UPDATE
AS
BEGIN
SET NOCOUNT ON;
if update(折扣)
begin
declare
@订单ID int,
@产品ID int,
@折扣 real
set @订单ID = (select 订单ID from inserted)
set @产品ID = (select 产品ID from inserted)
set @折扣 = (select 折扣 from inserted)
update 订单明细set 折扣=@折扣
where 订单ID=@订单ID and 产品ID=@产品ID
end
else
begin
print '只能更改折扣字段'
Raiserror('除了折扣字段以外的其余字段信息不能修改',16,5)
end
END
修改完触发器以后,再去修改其余非“折扣”字段的内容时,就会弹出错误提示,如图11.22所示,Raiserror的用法能够查看SQL Server 2005的帮助。
图11.22 显示错误信息
11.18 小结
触发器是与数据库和数据表相结合的特殊的存储过程,当数据表有Insert、Update、Delete操做或数据库有Create、Alter、Drop操做的时候,能够激活触发器,并运行其中的T-SQL语句。
在SQL Server 2005中触发器分为DML触发器和DDL触发器两种。其中DML触发器又分为After触发器和Instead Of触发器两种。After触发器是先修改记录后激活的触发器;Instead Of触发器是“取代”触发器。DDL触发器根据做用范围能够分为做用在数据库的触发器和做用在服务器的触发器两种。After触发器只能用于数据表中,而Instead Of触发器便可以用在数据表中,也能够用在视图中。
使用CREATE TRIGGER语句能够建立触发器,使用ALTER TRIGGER语句能够修改触发器,使用Drop Trigger语句能够删除触发器。触发器容许嵌套和递归,嵌套最多能够是32层。