第16章 触发器 学习导读 前面在介绍数据完整性时,提到SQL Server 2008提供了约束、默认值,以及触发器3类对象 ,用于保证数据的完整性,那时笔者只介绍了约 束和默认值,本章主要介绍触发器(Trigger)。 其实,触发器就是一类特殊的存储过程。触发器 在创建后,一般是在数据库事件的驱动下自动执 行的,即当SQL Server中某一个事件发生时, SQL Server将自动执行其相关联的触发器。 1 触发器概述 前面介绍的存储过程都是需要手工执行的, 即在SQL语句中,用EXECUTE语句执行相应 的存储过程;而触发器这类存储过程却由SQL Server自动执行。SQL Server之所以能够自动 执行触发器,是因为触发器在创建时,与特定 的SQL Server事件相关联。 依据触发器使用的SQL Server事件的不同, 触发器可分为:登录触发器、DDL触发器、 DML触发器。 2 登录触发器 登录触发器关联的事件是事件组 SecurityAudit的事件AuditLogin,即LOGIN事 件。如果用户遗忘了事件或事件组的概念,可 以参考前面介绍SQL Server Profiler跟踪的内容 。登录触发器是一种AFTER触发器,不过,登 录触发器不存在于特定的架构之下。 3 登录触发器的执行时机和作用 登录触发器的执行时机是用户登录到SQL Server, 具体的是在登录过程的身份验证结束之后至建立会话 之前。显然,如果身份验证失败,将不激发登录触发 器,所以触发器不能检测到用户对于登录的尝试,以 及失败的次数。不过,用户可以通过SQL Server的日 志了解到这些信息。 既然登录触发器执行于这个时期,那么就可以使用 登录触发器控制用户会话的建立过程,如限制SQL Server的用户名及其会话次数。 4 SQL登录触发器 所谓SQL登录触发器,是指用普通的SQL语 句编写的登录触发器,主要是区别使用CLR编 写的登录触发器。如果想要在登录触发器中使 用登录事件的信息,可以参看后面DDL中介绍 的EVENTDATA函数。 5 SQL登录触发器 1.创建SQL登录触发器 创建登录触发器的SQL语句为CREATE TRIGGER。该语句支持创建SQL语句编写的登 录触发器,也支持创建基于程序集的登录触发 器: CREATE TRIGGER trigger_name ON ALL SERVER [WITH [ENCRYPTION|EXECUTE AS][,…n] FOR LOGON AS {sql_statement [][,…n]} 6 SQL登录触发器 组成元素的意义: trigger_name是登录触发器的名称,不能以#或## 开头 ALL SERVER指示登录触发器的作用域是当前服 务器 WITH ENCRYPTION指示系统加密存储触发器的 文本 EXECUTE AS指示执行登录触发器的环境 sql_statement是登录触发器的核心 7 SQL登录触发器 创建一个登录触发器login_trigger,该登录触 发器的主要功能是将登录名为 PYW\Administrator的会话过程的信息记录到数 据库database_demo的表dbo.tb_logintrigger中 。 USE master GO CREATE TABLE database_demo.dbo.tb_logintrigger(id INT,name NCHAR(256),logintime DATETIME) GO CREATE TRIGGER login_trigger ON ALL SERVER WITH EXECUTE AS ‘PYW\Administrator’ FOR LOGON 8 SQL登录触发器 AS BEGIN INSERT INTO database_demo.dbo.tb_logintrigger(id,name,logintime) SELECT session_id,original_login_name,login_time FROM sys.dm_EXEC_sessions WHERE original_login_name=‘PYW\Administrator’ COMMIT END 9 SQL登录触发器 也可以通过使用系统视图sys.server_evnets 和sys.server_triggers查看登录触发器。 SELECT sysst.name,sysst.parent_class_desc,sysst.type_desc,sysse.ty pe_desc,sysse.is_trigger_event,sysse.event_group_type_des c FROM sys.server_events AS sysse LEFT JOIN sys.server_triggers AS sysst ON sysse.object_id=sysst.object_id WHERE sysse.type_desc=‘LOGON’ 10 SQL登录触发器 2.使用登录触发器 在使用CREATE TRIGGER语句创建SQL登录触发器 login_trigger后,SQL Server将自动使用该登录触发 器。在下一次登录到SQL Server数据库引擎时,就可 以看到效果。我们重新打开一个Studio程序。 待登录成功后,在数据库database_demo的表 dbo.tb_logintrigger中将包含此次登录的信息: USE database_demo GO SELECT id,name,logintime FROM database_demo.dbo.tb_logintrigger 11 SQL登录触发器 3.更改登录触发器 更改触发器的语句为ALTER TRIGGER: ALTER TRIGGER trigger_name ON ALL SERVER [WITH [ENCRYPTION|EXECUTE AS] [,…n]] FOR LOGON AS { sql_statement [] [,…n] } 12 SQL登录触发器 组成元素的意义: trigger_name是登录触发器的名称。不能以#或## 开头 ALL SERVER指定登录触发器的作用域是当前服 务器 WITH ENCRYPTION指示对创建文本加密存储 EXECUTE AS是执行登录触发器的环境 sql_statement是登录触发器的核心 13 SQL登录触发器 4.删除登录触发器 DROP TRIGGER trigger_name[,…n] ON ALL SERVER 14 登录触发器的常见错误 用户按照上面的SQL语句创建登录触发器,当然不 会产生问题,但是,如果用户自己创建登录触发器时 ,需要慎重。因为创建登录触发器与创建普通的表、 视图、存储过程或函数相比,具有一定的风险。 用户编写的登录触发器很容易造成SQL Server服务 不可访问的情况发生,这是因为登录触发器会影响正 常的会话过程。如果编写的登录触发器有些地方不正 确,那么将直接导致SQLCMD和Studio等不能访问 SQL Server服务。 15 登录触发器的常见错误 但此时SQL Server服务是良好的,只是SQL Server 服务在处理与用户的会话时,执行了错误的登录触发 器。此时必须使用DAC来连接SQL Server服务来删除 、禁用或修改该登录触发器。 16 DDL 触发器 在SQL Server 2008中,CREATE、ALTER、 DROP等语句常被称为数据定义语句,或数据定义语 言(Data Definition Language,DDL)。DDL触发器 就是与DDL相关联的一种特殊的存储过程。其实, DDL触发器是关联DLL语句对应的SQL Server事件。 这类操作被关联相应的DDL触发器后,再执行这类操 作时,SQL Server将会自动执行相关联的DDL触发器 。DDL触发器是一种AFTER触发器,不存在于特定的 架构之下,而存在于特定数据库中或整个SQL Server 实例中。 17 DDL触发器的执行时机和作用 DLL触发器的执行时机是其关联的操作结束 以后至下一个操作开始之前。所以,DDL触发 器可以帮助用户管理和控制一些与数据库对象 定义有关的操作,如管理和记录数据库对象结 构的更改。总地来说,DDL触发器主要用在以 下几方面。 处理对数据库对象结构的更改。 在数据库对象的结构发生更改后,执行处罚期内 定义的操作。 可以记录数据库对象结构的具体更改,以及相应 的SQL Server事件。 18 DLL触发器关联的事件组 DLL触发器关联的事件组依据其使用范围, 分为服务器范围内的事件组和数据库范围内的 事件组。可以通过SQL语句,查询当前SQL Server的事件组。 SELECT syst.parent_type,syst.type_name,syst.type FROM sys.trigger_event types AS syst WHERE syst.type_name LIKE ‘DDL_%’ GROUP BY syst.parent_type,syst.type,syst.type_name ORDER BY syst.parent_type,syst.type 19 DLL触发器关联的事件 在SQL Server中,与DLL触发器关联的事件 都属于预定义的事件组。下面将给出这些事件 的详细信息,包括事件的名称、type,以及 parent type。具体的,用户可以使用如下SQL 语句查询到与DLL触发器关联的事件。 SELECT syst.parent_type,syst.type_name,syst.type FROM sys.trigger_event_types AS syst WHERE syst.type_name NOT LIKE ‘DDL_%’ GROUP BY syst.parent_type,syst.type,syst.type_name ORDER BY syst.parent_type,syst.type_name 20 EVENTDATA函数:返回事件的XML结构 在编写DDL触发器时,可能需要获得服务器或数据 库事件的信息。这时,就需要使用EVENTDATA函数 ,其语法结构如下。 返回值=EVENTDATA() 其中返回值的类型为XML。对于不同事件, EVENTDATA函数返回的XML的属性和内容也不相同 ,相应事件返回的属性保存在events.xsd文件中,该 文件路径为C:\Program Files\Microsoft SQL Server\100\Tools\Binn\schemAS\SQL Server\2006\11\events\events.xsd。 21 EVENTDATA函数:返回事件的XML结构 可以在SQL Server 2008中通过打开文件查看 xsd文件。在SQL Server 中,EVENTDATA函 数返回事件的XML结构就是events.xsd文件中 定义的,确切地讲,是由文件中的 <xs:complexType></xs:complexType>单元定 义的。以CREATE TABLE事件为例,其在 events.xsd中对应的单元内容如下: 22 EVENTDATA函数:返回事件的XML结构 <xs:complexType name=“EVENT_INSTANCE_CREATE_TABLE”> <xs:sequence> <xs:element name=“EventType” type=“SSWNAMEType”/> <xs:element name=“PostTime” type=“xs:string”/> <xs:element name=“SPID” type=“xs:INT”/> <xs:element name=“ServerName” type=“PathType”/> <xs:element name=“LoginName” type=“SSWNAMEType”/> <xs:element name=“UserName” type=“SSWNAMEType”/> <xs:element name=“DatabaseName” type=“SSWNAMEType”/> <xs:element name=“SchemaName” type=“SSWNAMEType”/> <xs:element name=“ObjectName” type=“SSWNAMEType”/> <xs:element name=“ObjectType” type=“SSWNAMEType”/> <xs:element name=“TSQLCommand” type=“EventTag_TSQLCommand”/> </xs:sequence> </xs:complexType> 23 EVENTDATA函数:返回事件的XML结构 EVENTDATA函数返回的XML内容如下: <EVENT_INSTANCE> <EventType>CREATE_TABLE</EventType> <PostTime>2008-07-09T 10:03:26.560</PostTime> <SPID>54</SPID> <ServerName>PYW\MSSQL2008</ServerName> <LoginName>PYW\MSSQL2008</LoginName> <UserName>dbo</UserName> <DatabaseName>database_demo</DatabaseName> <SchemaName>dbo</SchemaName> <ObjectName>tb_CRL_DDL_trigger_test</ObjectName> <ObjectType>TABLE</ObjectType> <TSQLCommand> <SetOptions ANSI_NULLS=“ON” ANSI_NULL_DEFAULT=“ON” ANSI_PADDING=“ON” QUOTED_IDENTIFIER=“ON” ENCRYPTED=“FALSE”/> 24 EVENTDATA函数:返回事件的XML结构 <CommandText>CREATE TABLE [dbo].[tb_CRL_DDL_trigger_test]( [id][INT] NULL, [name] [NCHAR](10) NULL, [in_date] [datetime] NULL ) ON [PRIMARY] </CommandText> </TSQLCommand> </EVENT_INSTANCE> 这里主要使用XML的value()方法,从该XML中获取相应的值。由于 value()函数是从XML结构中获取信息的值,所以必须确保名称的大小写正 确,即在XML中是区分大小写的,否则将提取不到任何信息。 假设创建一个DDL触发器是使用CREATE_TABLE事件,那么 EVENTDATA函数返回的XML变量@data_xml可以通过value方法提取返回 的信息。 XML_value(‘(/EVENT_INSTANCE/UserName)[1]’,’NVARCHAR(2000)’) 25 SQL DDL触发器 所谓SQL DDL触发器,是指用普通的SQL语句编写 的DDL触发器,主要是区别于使用CLR编写的DDL触 发器。 1.创建SQL DDL触发器 CREATE TRIGGER语句支持SQL语句编写的DDL触 发器,也支持CLR触发器。 CREATE TRIGGER trigger_name ON ALL SERVER|DATABASE [WITH [ENCRYPTION|EXECUTE AS][,…n]] FOR {event_type|event_group} [,…n] AS {sql_statement [][,…n]} 26 SQL DDL触发器 组成元素的意义: trigger_name是DDL触发器的名称,不能以#和##开头 ALL SERVER是将DDL触发器的作用域指定为当前服务器 范围内,而DATABASE是指示DDL触发器保存于数据库范围 内 WITH ENCRYPTION指示将触发器文本加密存储 EXECUTE AS指定DDL触发器的执行环境 event_type是SQL Server事件的名称 event_group是SQL Server中预定义事件组的名称。该事 件组中包含一些特定的SQL Server事件 sql_statement是DDL触发器的核心 27 SQL DDL触发器 创建一个DDL触发器ddl_trigger。它将使用 EVENTDATA函数获得该触发器关联的事件信息,并 将其存储到数据库database_demo的表 dbo.ddl_trigger中。 USE database_demo GO CREATE TABLE tb_ddl_trigger (id INT identity(1,1),name NCHAR(2000),inTime datetime) GO CREATE TRIGGER ddl_trigger ON DATABASE FOR CREATE_TABLE 28 SQL DDL触发器 AS BEGIN DECLARE @data_xml XML SET @data_xml=EVENTDATA() INSERT INTO database_demo.dbo.tb_ddl_trigger(name,inTime) VALUES(@data_xml.value(‘(/EVENT_INSTANCE/UserName) [1]’,’NVARCHAR(2000)’),GETDATE()) END 由于创建触发器时使用了ON DATABASE,所以创建的DDL触 发器ddl_trigger被保存到了当前数据库中。如果使用ON ALL SERVER那么创建的DDL触发器将存储在服务器对象中。 29 SQL DDL触发器 2.使用DDL触发器 在创建DDL触发器后,SQL Server将自动使用 该触发器,再执行DDL操作,系统将根据事件 查询到相应的DDL触发器,并予以执行。 USE database_demo GO CREATE TABLE dbo.Table_1( id INT NOT NULL,hao NCHAR(10) NOT NULL) GO SELECT * FROM tb_ddl_trigger 30 SQL DDL触发器 3.更改DDL触发器 更改触发器也是用ALTER TRIGGER语句。 4.删除DDL触发器 删除触发器也是用DROP TRIGGER语句 31 SQL DDL触发器 5.查看DDL触发器的信息 SELECT syst.name AS ‘名称’,syst.parent_class_desc AS ‘作 用域’,syst.type_desc AS ‘种类’,syste.type_desc AS ‘事件 ’,syssm.definition AS ‘定义’ FROM sys.triggers AS syst LEFT JOIN sys.trigger_events AS syste ON syst.object_id=syste.object_id LEFT JOIN sys.sql_modules AS syssm ON syst.object_id=syssm.object_id 32 DML触发器 在SQL Server 2008中,INSERT、UPDATE 、DELETE等语句常被称为数据操纵语句,或 数据操纵语言(Data Manipulation Language ,DML)。DML触发器是与DML相关联的一种 特殊的存储过程。当DML操作被关联上相应的 DML触发器后,再执行这类操作时,SQL Server将会自动执行相关联的DML触发器。显 然,DML触发器属于特定的数据库,是对数据 库中表或视图操作的限制和拓展。 33 DML触发器类型 DML触发器有两种分类方法:一类是依据 DML触发器关联的DML语句,将DML触发器分 为INSERT触发器、UPDATE触发器和DELETE 触发器;另一类是依据DML执行过程,将DML 触发器分为AFTER触发器和INSTEAD OF触发 器。对于一个DML操作来说,其执行过程如下 。 (1)SQL Server将创建inserted表(INSERT 和UPDATE语句)或deleted表(DELETE语句 ) (2)SQL Server执行触发操作。 34 DML触发器类型 (3)SQL Server执行INSTEAD OF触发器。该触发器直接位 于触发操作之后,用于完成一些与触发操作类似的功能,因此 称为“替代”触发器。其实,INSTEAD OF触发器的出现是为了 完善和拓展触发器的部分功能。不过,最好不要在不熟悉的情 况下使用INSTEAD OF触发器。 (4)SQL Server处理约束。这些约束就是前面数据完整性里 介绍的内容。如果INSERT、UPDATE或DELETE等语句不能 通过约束的限制,那么SQL Server将撤销前面执行INSTEAD OF触发器的操作,并且就此退出此次DML操作,而不再执行下 面的AFTER触发器。 (5)SQL Server执行AFTER触发器。 35 DML触发器的作用 与DDL触发器相比,DML触发器的用途更为 广泛。这是因为SQL Server中DML语句要比 DDL多。所以,对于用户来说,掌握DML触发 器很重要。 DML触发器可以检查并避免错误的INSERT、 UPDATE、DELETE等DML操作,而且可以编写比 CHECK约束更为复杂的检验和限制。除此之外, DML触发器还具有CHECK约束没有的特性,如DML 触发器可以引用其他表中的列。 36 DML触发器的作用 DML触发器可以比较数据修改前后的表,并根据 两者的差异采取相应的操作,主要是使用inseted表 或deleted表 一个表中的多个同类DML触发器允许定义多个不 同的操作来响应同一个修改语句 INSTEAD OF触发器还可以更新视图中两个基表 的记录。这一点在普通视图中是不可能的。 37 DML触发器的创建语句 在SQL Server中,创建DML触发器的语句也 是CREATE TRIGGER。不过,语法结构与前 面创建DDL触发器有所不同。创建DML触发器 的CREATE TRIGGER语句的语法结构如下。 CREATE TRIGGER [ schema_name . ]trigger_name ON TABLE | VIEW [ WITH [ ENCRYPTION ] [ EXECUTE AS clause ] [ ,...n ] ] FOR | AFTER | INSTEAD OF [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] AS sql_statement [ ] [ ,...n ] 38 DML触发器的创建语句 组成元素的意义: schema_name.trigger_name是DML触发器的名 称 TABLE|VIEW是DML触发器操作的对象名称 WITH ENCRYPTION是对创建文本加密 EXECUTE AS是指定执行环境 FOR|AFTER|INSTEAD OF指定DML触发器的类 型,FOR和AFTER的含义相同。不能对视图视图定 义AFTER触发器。对于表或视图,每个DML语句最 多可以定义一个INSTEAD OF触发器 [INSERT][,][UPDATE][,][DELETE]是指定DML触 发器的类型 39 DML触发器的创建语句 sql_statement是DML触发器的核心,包含用于触 发DML触发器时相应的操作。有一些语句不允许或 不推荐在DML触发器中使用: ALTER DATABASE LOAD DATABASE LOAD LOG ALTER INDEX DBCC DBREINDEX CREATE DATABASE RESTORE DATABASE RESTORE LOG CREATE INDEX ALTER PARTITION FUNCTION 40 AFTER触发器 前面提到,AFTER触发器是DML操作触发器 的一类触发器。其实,如果对AFTER触发器进 行细分,那么AFTER触发器可以分为AFTERINSERT触发器、AFTER-UPDATE触发器以及 AFTER-DELETE触发器3类。 41 AFTER-INSERT触发器 所谓AFTER-INSERT触发器,是指该 INSERT触发器执行位于DML操作中AFTER触 发器的位置;而INSERT触发器是指当向表中 插入新记录数据时,SQL Server自动执行特殊 的存储过程。 42 AFTER-INSERT触发器 1.创建AFTER-INSERT触发器 在database_demo中创建一个表tb_1并在其上创建一 个AFTER-INSERT触发器。 USE database_demo GO CREATE TABLE tb_1(id INT,name NCHAR(10)) GO CREATE TRIGGER dbo.insert_dml_trigger ON tb_1 AFTER INSERT AS BEGIN SELECT id,name,GETDATE() AS ‘时间’ FROM inserted END 43 AFTER-INSERT触发器 2.查看AFTER-INSERT触发器 USE database_demo GO SELECT syst.name AS ‘DML触发器’,syso.name AS ‘ 所在的表’ FROM sys.triggers AS syst INNER JOIN sys.objects AS syso ON syst.parent_id=syso.object_id 当然也可以从Studio查看 44 AFTER-INSERT触发器 3.执行AFTER-INSERT触发器 向tb_1中插入一条记录时,会触发AFTERINSERT触发器。 USE database_demo GO INSERT INTO tb_1 VALUES(11,’LXP’) 45 AFTER-UPDATE触发器 所谓AFTER-UPDATE触发器,是指该 UPDATE触发器执行位于DML操作中AFTER触 发器的位置;而UPDATE触发器是指当更新表 中记录数据时,SQL Server自动执行特殊的存 储过程。 46 AFTER-UPDATE触发器 1.创建AFTER-UPDATE触发器 USE database_demo GO CREATE TABLE tb_2(id INT,name NCHAR(10)) GO INSERT INTO tb_2 VALUES(11,’LXP’) GO CREATE TRIGGER dbo.update_dml_trigger ON tb_2 AFTER UPDATE AS BEGIN SELECT id,name,GETDATE() AS ‘时间’ FROM inserted END 47 AFTER-UPDATE触发器 2.查看AFTER-UPDATE触发器 USE database_demo GO SELECT syst.name AS ‘DML触发器’,syso.name AS ‘ 所在的表’ FROM sys.triggers AS syst INNER JOIN sys.objects AS syso ON syst.parent_id=syso.object_id 48 AFTER-UPDATE触发器 3.执行AFTER-UPDATE触发器 USE database_demo GO UPDATE tb_2 SET name=‘LIN’ WHERE id=11 49 AFTER-DELETE触发器 所谓AFTER-DELETE触发器,是指该 DELETE触发器执行位于DML操作中AFTER触 发器的位置;而DELETE触发器是指当更新表 中记录数据时,SQL Server自动执行特殊的存 储过程。 50 AFTER-DELETE触发器 1.创建AFTER-DELETE触发器 USE database_demo GO CREATE TABLE tb_3(id INT,name NCHAR(10)) GO CREATE TRIGGER dbo.delete_dml_trigger ON tb_3 AFTER DELETE AS BEGIN SELECT id,name,GETDATE() AS ‘时间’ FROM deleted END 51 AFTER-DELETE触发器 2.查看AFTER-DELETE触发器 USE database_demo GO SELECT syst.name AS ‘DML触发器 ’,syso.name AS ‘所在的表’ FROM sys.triggers AS syst INNER JOIN sys.objects AS syso ON syst.parent_id=syso.object_id 52 AFTER-DELETE触发器 3.执行AFTER-DELETE触发器 USE database_demo GO DELETE FROM tb_3 WHERE id=11 53 INSTEAD OF触发器 前面已经介绍了创建AFTER触发器,不过, AFTER触发器仅支持表。本节介绍的INSTEAD OF触发器也包括AFTER-INSERT触发器、 AFTER-UPDATE触发器和AFTER-DELETE触 发器,但它除支持表外,还支持视图。由于前 面已经详细介绍了AFTER触发器的创建、查看 以及使用方法,因此这里不再赘述。本节将通 过示例来介绍为视图创建INSTEAD OF触发器 。 54 INSTEAD OF触发器 创建两个基表tb_4和tb_5,基于这两个表创 建视图v_1,然后为v_1创建一个INSTEAD OF 类型的INSERT 触发器。 USE database_demo GO CREATE TABLE tb_4(id INT,name NCHAR(10)) GO CREATE TABLE tb_5(id INT,salary INT) GO CREATE VIEW v_1(id,name,salary) AS 55 INSTEAD OF触发器 SELECT tb_4.id,tb_4.name,tb_5.salary FROM tb_4 INNER JOIN tb_5 ON tb_4.id=tb_5.id GO CREATE TRIGGER dbo.insert_INSTEADOF_dml_trigger ON v_1 INSTEAD OF INSERT AS BEGIN INSERT INTO tb_4 SELECT id,name FROM inserted 56 INSTEAD OF触发器 INSERT INTO tb_5 SELECT id,salary FROM inserted SELECT * FROM tb_4 SELECT * FROM tb_5 END GO 57 INSTEAD OF触发器 向v_1的两个基表同时插入记录,触发器会返 回新插入的记录信息。 USE database_demo GO INSERT INTO v_1 VALUES(1,’LIN’,1200) 58 小结 本章主要介绍触发器的使用。触发器是数据 约束很重要的一个技术手段,在更新某个数 据时还要修改谁、修改什么,主要用触发器 来完成。由于在系统优先级方面,触发器高 于存储过程,所以在对重要数据更新时,一 般都使用触发器,但滥用触发器也会降低系 统速度。触发器是双刃剑,在使用时,一定 要谨慎。 59
© Copyright 2024 ExpyDoc