触发器包含:dml触发器和ddl触发器。
dml触发器包含了用于对表或视图的insert、update、delete操作做出响应的T-SQL代码,而ddl触发器对服务器或数据库事件做出响应而不是数据修改。
触发器能够自动响应某种行为,所以对于必须对某种行为做出业务级别响应的情况,触发器很合适。
在用触发器时,需要注意的:
1、触发器通常比较隐蔽,很容易被忘记,在检查性能或逻辑问题的时候,经常会忘记触发器是在后台执行的,要确保在文档中记录了触发器。
2、如果所有的数据修改流程都通过存储过程完成,那么尽可能不要使用触发器。
3、始终需要保证性能,确保能快速执行且没有bug。长时间运行的触发器会严重减慢数据修改操作,所有在数据修改比较频繁的数据库中使用触发器需要特别小心。
4、不记录日志的更新不会引起dml触发器的触发,如:writetext,truncate table、批量导入操作。
5、约束通常比dml触发器运行更快,因此如果约束能满足业务的需要,则使用约束来代替。由于after触发器是在数据修改之后触发的,所以不能防止违反约束。
6、不允许在触发器中使用select语句来返回结果集。
create table dbo.t (vid int not null primary key, v varchar(100) , vv varchar(10) ) select * into dbo.t_insert from dbo.t select * into dbo.t_update from dbo.t select * into dbo.t_delete from dbo.t insert into dbo.t(vid,v,vv) values(1,'a','aaa'), (2,'b','bbb'), (3,'c','ccc'), (4,'d','ddd'), (5,'e','eee') --创建after dml触发器 create trigger dbo.t_after on dbo.t after insert,delete as insert into dbo.t_insert select * from inserted insert into dbo.t_delete select * from deleted go --触发 insert into dbo.t values(6,'f','ffff') --发现已经添加到表中 select * from dbo.t_insert
--1.创建instead of触发器 create trigger dbo.t_instead_of on dbo.t with encryption instead of insert not for replication as declare @v varchar(100) set @v = '' select @v = V from inserted if (@v = 'a') rollback --回滚 /*========================================= 引用插入的列只能在select语句中使用, 而不能单独引用: if(inserted.v = 'a') rollback 无法绑定由多个部分组成的标识符 "inserted.v" ===========================================*/ go --事务在触发器中结束。批处理已中止。 insert into dbo.t values(7,'a','aaa') --2.修改触发器,根据修改列控制触发器 alter trigger dbo.t_instead_of on dbo.t with encryption instead of insert not for replication as declare @v varchar(100) set @v = '' select @v = V from inserted --如果在v列上执行insert或update,那么update(v)返回true if (@v = 'a' or UPDATE(v)) rollback --回滚 /*========================================= 引用插入的列只能在select语句中使用, 而不能单独引用: if(inserted.v = 'a') rollback 无法绑定由多个部分组成的标识符 "inserted.v" ===========================================*/ go --由于更新了v列,所以事务在触发器中结束。批处理已中止。 insert into dbo.t values(7,'g','g')
--1.创建数据库级别触发器 create trigger wcc on database for create_table as raiserror( 'A error occur,please retry again!',16,1) rollback go /*======================================= 消息 50000,级别 16,状态 1,过程 wcc, A error occur,please retry again! 消息 3609,级别 16,状态 2,第 1 行 事务在触发器中结束。批处理已中止。 =========================================*/ create table www(vid int not null) --2.1在master数据库中建立服务器级别跟踪表 use master go create table server_eventdata (eventdata xml, principal_user nvarchar(100), login_user nvarchar(100) ) --2.2建立服务器级别触发器 create trigger gyy_server on all server for create_table,drop_table,create_index as insert into server_eventdata select EVENTDATA(),USER,SUSER_NAME() go --2.3会触发服务器级别触发器 create table www(vid int not null) --查看记录的事件 select * from server_eventdata
触发器元数据、管理触发器
--1.dml触发器元数据 select o.name, t.name, t.parent_class_desc, --对象或列 t.type, t.type_desc, --触发器 is_not_for_replication, --在对表进行复制修改时不执行触发器 is_instead_of_trigger, --是否是instead of触发器 s.definition from sys.triggers t inner join sys.objects o on t.parent_id = o.object_id inner join sys.sql_modules s on s.object_id = t.object_id --2.数据库级别ddl触发器元数据 select t.name, T.parent_class, T.parent_class_desc, T.type_desc, S.definition from sys.triggers t inner join sys.sql_modules s on s.object_id = t.object_id where parent_id =0 and parent_class_desc ='DATABASE' --3.服务器级别ddl触发器元数据 SELECT ST.name, parent_class, parent_class_desc, type_desc, SSM.definition FROM SYS.server_triggers ST INNER JOIN SYS.server_sql_modules SSM ON ST.object_id = SSM.OBJECT_ID WHERE parent_class_desc = 'SERVER' --4.1限制触发器嵌套,服务器范围的选项 use master go --禁止触发器嵌套 exec sp_configure 'nested triggers',0 reconfigure with override go --启用触发器嵌套 exec sp_configure 'nested triggers',1 reconfigure go --4.2控制触发器递归,数据库范围的选项 --允许递归,after触发器仍然受到32层嵌套的限制 alter database wcc set recursive_triggers on --禁止递归 alter database wcc set revursive_triggers off --查看数据库是否允许触发器递归 select is_recursive_triggers_on from sys.databases --5.1禁用某个表的某个触发器 disable trigger dbo.t_after on dbo.t --5.2禁用某个表的所有触发器 disable trigger all on dbo.t --5.3禁用某个数据库的某个数据库触发器 disable trigger wcc on database --5.4禁用某个数据库的所有数据库触发器 disable trigger all on database --5.5禁用服务器上的某个触发器 disable trigger gyy_server on all server --5.6禁用服务器上所有的服务器级别触发器 disable trigger all on all server --5.7启用服务器上所有的服务器级别触发器 enable trigger all on all server --5.8删除dml触发器 drop trigger dbo.t_after --5.9删除ddl数据库级别触发器 drop trigger wc on database --5.10删除ddl服务器级别触发器 drop trigger gyy_server on all server --6.设置触发器触发的次序 create trigger dbo.tt_1 on dbo.t after insert as print 'dbo.tt_1' go create trigger dbo.tt_2 on dbo.t after insert as print 'dbo.tt_2' go create trigger dbo.tt_3 on dbo.t after insert as print 'dbo.tt_3' go --设置触发器触发的次序 exec sp_settriggerorder @triggername = 'tt_1', --触发器名称 @order ='first', --指定的次序 @stmttype = 'insert' --触发器类型 exec sp_settriggerorder @triggername = 'tt_2', @order = 'last', @stmttype = 'insert' --触发多个触发器 insert into dbo.t values(10,'w','www') /*====================================== 输出消息: dbo.tt_1 dbo.tt_3 dbo.tt_2 ========================================*/
如果有10个表,需要union all这10个表的数据来查询,那么通过建立一个视图,然后要对视图进行插入操作,那么必须有几个必要的条件:
1.每个表都有主键
2.每个表必须有check约束,来指定任何一条数据,到底要插入到那个表中。
下面为ddl触发器的实例:
--2.1在master数据库中建立服务器级别跟踪表 use master go create table server_eventdata (eventdata xml, principal_user nvarchar(100), login_user nvarchar(100) ) go /* select * from sys.trigger_event_types where type_name like '%grant%' or type_name like '%deny%' or type_name like '%revoke%' */ --2.2建立服务器级别触发器 create trigger gyy_server on all server for GRANT_SERVER, DENY_SERVER, REVOKE_SERVER, GRANT_DATABASE, DENY_DATABASE, REVOKE_DATABASE as insert into server_eventdata select EVENTDATA(),USER,SUSER_NAME() go --2.3 create database wc go use wc go create table dbo.wc_table(v int) insert into dbo.wc_table values(1) go use wc go grant select on wc.dbo.wc_table to public go --查看记录的事件 select EVENTDATA, eventdata.value('(/EVENT_INSTANCE/EventType)[1]','nvarchar(100)') as '事件类型', eventdata.value('(/EVENT_INSTANCE/TSQLCommand)[1]','nvarchar(100)') as 'sql授权语句', '登录名' + eventdata.value('(/EVENT_INSTANCE/LoginName)[1]','nvarchar(100)') + '用户名' + eventdata.value('(/EVENT_INSTANCE/UserName)[1]','nvarchar(100)') + ',授予者'+ eventdata.value('(/EVENT_INSTANCE/Grantor)[1]','nvarchar(100)') + ' 把类型为:' + eventdata.value('(/EVENT_INSTANCE/ObjectType)[1]','nvarchar(100)') + '的对象' + eventdata.value('(/EVENT_INSTANCE/DatabaseName)[1]','nvarchar(100)') + '.' + eventdata.value('(/EVENT_INSTANCE/SchemaName)[1]','nvarchar(100)') + '.' + eventdata.value('(/EVENT_INSTANCE/ObjectName)[1]','nvarchar(100)') + '的' + eventdata.value('(/EVENT_INSTANCE/Permissions/Permission)[1]','nvarchar(100)') +'权限授予给' + eventdata.value('(/EVENT_INSTANCE/Grantees)[1]','nvarchar(100)') from master.dbo.server_eventdata t
下面是一段引用自邹建的代码,通过链接服务器+触发器,实现数据同步的代码,很有借鉴作用:
/* 作者:邹建 */ /*--同步两个数据库的示例 有数据 srv1.库名..author有字段:id,name,phone, srv2.库名..author有字段:id,name,telphone,adress 要求: srv1.库名..author增加记录则srv1.库名..author记录增加 srv1.库名..author的phone字段更新,则srv1.库名..author对应字段telphone更新 --*/ --大致的处理步骤 --1.在 srv1 上创建连接服务器,以便在 srv1 中操作 srv2,实现同步 exec sp_addlinkedserver 'srv2','','SQLOLEDB','srv2的sql实例名或ip' exec sp_addlinkedsrvlogin 'srv2','false',null,'用户名','密码' go --2.在 srv1 和 srv2 这两台电脑中,启动 msdtc(分布式事务处理服务),并且设置为自动启动 我的电脑--控制面板--管理工具--服务--右键 Distributed Transaction Coordinator--属性--启动--并将启动类型设置为自动启动 go --3.实现同步处理 --a.在srv1..author中创建触发器,实现数据即时同步 --新增同步 create trigger tr_insert_author on author for insert as set xact_abort on insert srv2.库名.dbo.author(id,name,telphone) select id,name,telphone from inserted go --修改同步 create trigger tr_update_author on author for update as set xact_abort on update b set name=i.name,telphone=i.telphone from srv2.库名.dbo.author b,inserted i where b.id=i.id go --删除同步 create trigger tr_delete_author on author for delete as set xact_abort on delete b from srv2.库名.dbo.author b,deleted d where b.id=d.id go