• 【SQL Server学习笔记】DML触发器、DDL触发器


    触发器包含: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  
       
       
    


     

  • 相关阅读:
    程序员之痛:几千行代码能搞定的为什么要写几万行?
    python基础===新式类与经典类
    python基础===装饰器@property 的扩展
    java===java基础学习(16)---final
    java===java基础学习(15)---抽象,接口
    python基础===继承和多继承
    python基础===类的私有属性(伪私有)
    python基础===创建大量对象是节省内存方法
    java===java习题---Josephu问题
    java===java基础学习(14)---封装
  • 原文地址:https://www.cnblogs.com/momogua/p/8304612.html
Copyright © 2020-2023  润新知