一、触发器概念

1.1、触发器特征

        1.1.1、触发器是在对表进行增、删、改时,自动执行的存储过程。触发器常用于强制业务规则,它是一种高级约束,通过事件进行触发而被执行。

        1.1.2、触发器是一个特殊的事务单元,可以引用其他表中的列执行特殊的业务规则或数据逻辑关系。当出现错误时,可以执行rollback transaction操作将整个触发器以及触发它的T-SQL语句一并回滚(不需显示声明begin transaction)。

        1.1.3、每个触发器将用到的两个临时表

deleted 临时表:用于临时存放被删除的记录行副本(包括delete和update语句所影响的数据行);
                  注意:被删除的记录行,首先从原始表中删除,并保存到触发器表。然后从触发器表中删除,再保存到deleted表。
inserted 临时表:用于临时存放插入的记录行副本(包括insert和update语句所影响的数据行);
deleted表和inserted表的特征:
    > 这两个表的表结构与该触发器作用的表相同;
    > 这两个表是逻辑表,并且由系统管理;
    > 这两个表是动态驻留在内存中的(不是存储在数据库中),当触发器工作完成后,它们也被删除;
    > 这两个表是只读的,即只能运用select语句查看(用户不能直接更改);
        1.1.4、所创建的触发器(insert、delete、update)是在原表数据行已经修改完成后再触发。所以,触发器是在约束检查之后才执行。

1.2、什么时候用触发器?

a、实现主外键关系所不能保证的复杂参照完整性和数据的一致性。
    不过,通过“级联引用完整性约束”可以更有效地执行这些更改。
b、防止恶意或错误的 INSERT、UPDATE 以及 DELETE 操作,并强制执行比 CHECK 约束定义的限制更为复杂的其他限制。
   > 与 CHECK 约束不同(check约束只能引用自身表中的列),DML触发器可以引用其他表中的列
   > 触发器可以完成所有约束的功能,但不一定是最佳方案;
   > 触发器能够使用自定义信息和较为复杂的错误处理
c、DML 触发器可以评估数据修改前后表的状态,并根据该差异采取措施。
d、一个表中的同一个修改语句的DML触发器,允许被多个不同的操作(INSERT、UPDATE 或 DELETE)来响应

1.3、触发器的类型:
insert 触发器;(略)
delete 触发器;(略)
update 触发器:在修改表中记录行或某列数据时触发执行;
注意:update(列)函数:实现检测某列是否被修改。
update 更新操作分为两步:
首先,“删除”更改前原有数据行:删除的原有数据行将复制到deleted临时表中;
然后,“插入”更改后的新数据行:插入新数据行到原始表,同时将新数据行保存到inserted临时表和触发器表中;
1.4、创建触发器的注意点:
     1.4.1、create trigger必须是批处理(go)的第一条语句;
     1.4.2、一个触发器语句只能用到一个表或一个视图中;
   on 表名/ 视图名
     1.4.3、一个触发器语句可以执行多个操作;
   for delete,insert,update -- 无先后顺序的任意组合
     1.4.4、建议DML触发器不返回任何结果。这是因为对这些返回结果的特殊处理必须写入每个允许对触发器表进行修改的应用程序中。
     若要防止从 DML 触发器返回任何结果,请不要在触发器定义中包含select语句或变量赋值;
     如果必须在触发器中进行变量赋值,则应该在触发器被触发之前使用set nocount on语句以避免返回任何结果集;
注意:未来版本的SQL Server 中,将会删除从触发器返回结果集的功能。
      1.4.5、如果“触发器表”本身也存在约束,则在执行insert、delete、update触发器前,首先会检查“触发器表”上存在的约束。如果不满足约束,则不会执行其insert、delete、update触发器。

1.5、查看当前数据库中的所有触发器

  1. select * from sys.triggers

二、触发器实例

2.1、初始化表

  1. --------------- 初始化环境 ---------------
  2.  
  3. create database TriggerDatabase
  4. use TriggerDatabase
  5. go
  6.  
  7. if exists(select * from sysobjects where name='bank')
  8. drop table bank
  9.  
  10. create table bank -- 账户信息表
  11. (
  12. userName varchar(10) not null, --顾客名
  13. cardID varchar(10) not null, --卡号
  14. currentMoney money not null --当前余额
  15. )
  16.  
  17. if exists(select * from sysobjects where name='transInfo')
  18. drop table transInfo
  19.  
  20. create table transInfo --交易信息表
  21. (
  22. cardID varchar(10) not null, --卡号
  23. transType char(4) not null, --交易类型(存入/支取)
  24. transMoney money not null, --交易金额
  25. transDate datetime not null --交易日期
  26. )
  27. go
  28.  
  29. --------------- 添加约束 ---------------
  30. alter table bank
  31. add constraint CK_currentMoney check(currentMoney>=1);
  32.  
  33. alter table transInfo
  34. add constraint DF_transDate default(getdate()) for transDate;
  35.  
  36. alter table transInfo
  37. add constraint CK_transType check(transType in('支取','存入'));
  38.  
  39. --------------- 添加测试数据 ---------------
  40. /* 张三 1000元 */
  41. insert into bank(userName,cardID,currentMoney)
  42. values('张三','1001 0001',1000);
  43. /* 李四 1元 */
  44. insert into bank(userName,cardID,currentMoney)
  45. values('李四','1001 0002',1);
  46. /* 张三 支取 200元 */
  47. insert into transInfo(cardID,transType,transMoney)
  48. values('1001 0001','支取',200);
  49.  
  50. --------------- 查看结果 ---------------
  51. select * from bank;
  52. select * from transInfo;
  53. go

2.2、触发器格式

  1. create trigger [ schema_name. ] -- 触发器所属架构
  2. trigger_name -- 触发器名称
  3. on { table | view } -- 触发器的表或视图
  4. [ with encryption ] -- 加密dml触发器定义(后面详解)
  5. { for | after }
  6. /* after:只有在触发它的SQL语句执行成功后才能激发。
  7. (只能对“表”定义after) */
  8. { insert,update,delete }
  9. as
  10. /* SQL语句... */
  11. go
  12.  
  13. --查看当前数据库中的所有触发器
  14. select * from sys.triggers

2.3、Insert 触发器

  1. ------------------ insert 触发器 ------------------
  2. use TriggerDatabase
  3. go
  4. if exists(select * from sysobjects
  5. where name='trig_insert_transInfo')
  6. drop trigger trig_insert_transInfo
  7. go
  8.  
  9. -- create trigger必须是批处理(go)的第一句
  10.  
  11. create trigger trig_insert_transInfo
  12. on transInfo for insert
  13. as
  14. declare @_transType char(4), --定义变量
  15. @_transMoney money,
  16. @_cardID char(10),
  17. @balance money --所剩余额
  18.  
  19. -- inserted临时表中获取记录值
  20. select @_transType = transType,
  21. @_transMoney = transMoney,
  22. @_cardID = cardID
  23. from inserted
  1. if(@_transType = '支取')
  2. update bank set currentMoney=currentMoney-@_transMoney
  3. where cardID = @_cardID;
  4. else
  5. update bank set currentMoney=currentMoney+@_transMoney
  6. where cardID = @_cardID;
  7.  
  8. --显示交易金额
  9. print '交易成功! 交易金额:'
  10. + convert(varchar(20),@_transMoney)
  11.  
  12. --显示所剩余额
  13. select @balance = currentMoney from bank
  14. where cardId = @_cardID
  15.  
  16. print '卡号:'+@_cardID
  17. + ' 余额:'+convert(varchar(20),@balance);
  18. go
  19.  
  20. ------------------ 测试触发器 ------------------
  21. -- delete from transInfo
  22. set nocount on --不显示T-SQL影响的记录行数
  23.  
  24. insert into transInfo(cardID,transType,transMoney)
  25. values('1001 0001','支取',200);
  26. insert into transInfo(cardID,transType,transMoney)
  27. values('1001 0001','存入',10000);
  28. --查看结果
  29. select * from bank
  30. select * from transInfo
  31. 复制代码

2.4、delete 触发器

  1. /* 实现: 当清除'交易信息表'的数据时,
  2. 自动备份被清除的数据到backupTable表中
  3. */
  4.  
  5. ------------------ delete 触发器 ------------------
  6. use TriggerDatabase
  7. go
  8.  
  9. if exists (select * from sysobjects
  10. where name='trig_delete_transInfo')
  11. drop trigger trig_delete_transInfo
  12. go
  13.  
  14. create trigger trig_delete_transInfo
  15. on transInfo after delete -- for | after
  16. as
  17. print '开始备份数据,请稍后......'
  18. -- 如果数据库中,不存在 backupTable
  19. if not exists(select * from sysobjects
  20. where name='backupTable')
  21. select * into backupTable from deleted --deleted临时表
  22. else
  23. insert into backupTable select * from deleted
  24.  
  25. print '备份成功,备份表 backupTable 中的数据为:'
  26. select * from backupTable;
  27. go
  28.  
  29. ------------------ 测试触发器 ------------------
  30. set nocount on
  31.  
  32. delete from transInfo; --测试
  33.  
  34. --查看结果
  35. select * from transInfo
  36. select * from backupTable

2.5、update 触发器

  1. ------------------ update 触发器 ------------------
  2. use TriggerDatabase
  3. go
  4.  
  5. if exists (select * from sysobjects
  6. where name='trig_update_bank')
  7. drop trigger trig_update_bank
  8. go
  9.  
  10. create trigger trig_update_bank
  11. on bank for update --在bank表上创建update触发器
  12. as
  13. declare @beforeMoney money,
  14. @afterMoney money,
  15. @currentTrans money --当前交易金额
  16.  
  17. --从deleted临时表,获取交易前的余额
  18. select @beforeMoney = currentMoney from deleted;
  19. --从inserted临时表,获取交易后的余额
  20. select @afterMoney = currentMoney from inserted;
  21.  
  22. if abs(@afterMoney-@beforeMoney) > 2000
  23. begin
  24. print '当前交易金额为:' +
  25. convert(varchar(20),abs(@afterMoney-@beforeMoney))
  26. -- 自定义错误消息
  27. raiserror('每次交易金额不能超过2000元,交易失败!',16,1)
  28.  
  29. rollback transaction --回滚事务,撤销交易!
  30. /* 注意:
  31. 触发器是一个特殊的事务单元
  32. 不需显示声明begin transaction
  33. */
  34. end
  35. go
  36.  
  37. ------------------ 测试触发器 ------------------
  38. set nocount on
  39.  
  40. --测试1: bank表触发 update触发器
  41. update bank set currentMoney = currentMoney + 25000
  42. where cardID = '1001 0001'
  43.  
  44. --测试2: 通过 transInfo表的 trig_insert_transInfo触发器
  45. -- 间接触发 bank表的 trig_update_bank触发器
  46.  
  47. insert into transInfo(cardID,transType,transMoney)
  48. values('1001 0001','存入',10000);
  49.  
  50. --查看结果
  51. select * from bank
  52. select * from transInfo

2.6、MSDN参考

     2.6.1、加密 dml触发器定义
      若要确保其他用户不能查看触发器定义,可以使用with encryption子句加密 dml 触发器。
      使用with encryption子句后,触发器定义即以无法读取的格式进行存储。
      触发器定义加密后,无法进行解密。且任何人都无法进行查看,包括触发器的所有者和系统管理员。

     2.6.2、update() 函数:
      可用于确定 insert或 update语句是否影响表中的特定列。
      无论何时为列赋值,该函数都将返回 true。

使用if update() 子句示例:

  1. create table testTable(a int null, b int null)
  2. go
  3. create trigger my_trig
  4. on testTable for insert
  5. as
  6. if update(b)
  7. print '列b已被修改!'
  8. go
  9.  
  10. insert into testTable(b) values(123);
  11.  
  12. -- drop table testTable

注意: 
      由于 delete 语句无法只对某列进行删除,
      因此不能将if update()子句应用于delete 语句。

     2.6.3、columns_updated() 函数:
      也可用于检查 insert或 update语句更新了表中的哪些列。
      此函数使用整数位掩码指定要测试的列。

使用columns_updated() 函数示例:

  1. create table testTable2(a int null, b int null)
  2. go
  3.  
  4. create trigger my_trig2
  5. on testTable2 for insert
  6. as
  7. if ( columns_updated() & 2 = 2 )
  8. print '列b已被修改!'
  9. go
  10.  
  11. insert into testTable2(b) values(123);
  12.  
  13. -- drop table testTable2

转载:http://www.cnblogs.com/xugang/archive/2010/02/20/1669619.html

浅谈 Sql Server 触发器的更多相关文章

  1. 【SqlServer系列】浅谈SQL Server事务与锁(上篇)

    一  概述 在数据库方面,对于非DBA的程序员来说,事务与锁是一大难点,针对该难点,本篇文章视图采用图文的方式来与大家一起探讨. “浅谈SQL Server 事务与锁”这个专题共分两篇,上篇主讲事务及 ...

  2. 浅谈SQL Server内部运行机制

    对于已经很熟悉T-SQL的读者,或者对于较专业的DBA来说,逻辑的增删改查,或者较复杂的SQL语句,都是非常简单的,不存在任何挑战,不值得一提,那么,SQL的哪些方面是他们的挑战 或者软肋呢? 那就是 ...

  3. 浅谈SQL Server数据内部表现形式

    在上篇文章 浅谈SQL Server内部运行机制 中,与大家分享了SQL Server内部运行机制,通过上次的分享,相信大家已经能解决如下几个问题: 1.SQL Server 体系结构由哪几部分组成? ...

  4. c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程

    c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...

  5. 浅谈SQL Server事务与锁(上篇)

    一  概述 在数据库方面,对于非DBA的程序员来说,事务与锁是一大难点,针对该难点,本篇文章试图采用图文的方式来与大家一起探讨. “浅谈SQL Server 事务与锁”这个专题共分两篇,上篇主讲事务及 ...

  6. 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架

    简介 SQL Server中的事务日志无疑是SQL Server中最重要的部分之一.因为SQL SERVER利用事务日志来确保持久性(Durability)和事务回滚(Rollback).从而还部分确 ...

  7. 浅谈SQL Server 对于内存的管理

    简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...

  8. (转)浅谈SQL Server 对于内存的管理

    简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...

  9. 浅谈SQL Server中的三种物理连接操作

    简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...

随机推荐

  1. 搭建iSCSI共享IPSAN

    iSCSI(internet SCSI)是一个供硬件设备使用的.可以在IP协议的上层运行的SCSI指令集,这种指令集合可以实现在IP网络上运行SCSI协议,使其能够在诸如高速千兆以太网上进行路由选择. ...

  2. Oracle Data Provider for .NET Support for Microsoft .NET Core

    Oracle Data Provider for .NET Support for Microsoft .NET Core的官方地址,记录下来,按照官方描述,会在2017年底左右发布,暂时还没有看到相 ...

  3. gulp 输出到同一目录

    gulp.task('jsx', function () { var src='app/script/**/*.jsx'; // src='app/script/components/selloff/ ...

  4. 20155215 《Java程序设计》实验一(Java开发环境的熟悉)实验报告

    20155215 <Java程序设计>实验一(Java开发环境的熟悉)实验报告 实验要求 1.没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑 ...

  5. 20155231 java实验一 Java开发环境的熟悉

    20155231 java实验一 Java开发环境的熟悉 实验要求 没有Linux基础的同学建议先学习<Linux基础入门(新版)><Vim编辑器> 课程: 完成实验.撰写实验 ...

  6. 柴柴随笔第三篇:安装虚拟机以及Linux基础入门

    虚拟机的安装 老师提供的作业指南给了我莫大的帮助,一步一步按着其中操作提示和网址链接,我首先下好了VM,也创建好了自己的第一台虚拟机. 接着按照步骤安装了Ubuntu到我的虚拟机. 到此,一切都顺风顺 ...

  7. 【HDU3117】Fibonacci Numbers

    [HDU3117]Fibonacci Numbers 题面 求斐波那契数列的第\(n\)项的前四位及后四位. 其中\(0\leq n<2^{32}\) 题解 前置知识:线性常系数齐次递推 其实后 ...

  8. CF 987 D. Fair

    D. Fair http://codeforces.com/contest/987/problem/D 题意: n个城镇m条道路,(保证没有重边,两个城镇间可以到达),每个城镇拥有的特产ai(可能多个 ...

  9. 我们一起学习WCF 第四篇单通讯和双向通讯

    前言:由于个人原因很久没有更新这个系列了,我会继续的更新这系列的文章.这一章是单向和双向通讯.所谓的单向就是只有发送却没有回复,双向是既有发送还有回复.就是有来无往代表单向,礼尚往来表示双向.下面我用 ...

  10. EasyUI validatebox 自定义ajax验证用户名是否已存在

    <td><input type="text" id="userName" name="userName" class=&q ...