浅谈 Sql Server 触发器
一、触发器概念
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、查看当前数据库中的所有触发器
select * from sys.triggers
二、触发器实例
2.1、初始化表
--------------- 初始化环境 --------------- create database TriggerDatabase
use TriggerDatabase
go if exists(select * from sysobjects where name='bank')
drop table bank create table bank -- 账户信息表
(
userName varchar(10) not null, --顾客名
cardID varchar(10) not null, --卡号
currentMoney money not null --当前余额
) if exists(select * from sysobjects where name='transInfo')
drop table transInfo create table transInfo --交易信息表
(
cardID varchar(10) not null, --卡号
transType char(4) not null, --交易类型(存入/支取)
transMoney money not null, --交易金额
transDate datetime not null --交易日期
)
go --------------- 添加约束 ---------------
alter table bank
add constraint CK_currentMoney check(currentMoney>=1); alter table transInfo
add constraint DF_transDate default(getdate()) for transDate; alter table transInfo
add constraint CK_transType check(transType in('支取','存入')); --------------- 添加测试数据 ---------------
/* 张三 1000元 */
insert into bank(userName,cardID,currentMoney)
values('张三','1001 0001',1000);
/* 李四 1元 */
insert into bank(userName,cardID,currentMoney)
values('李四','1001 0002',1);
/* 张三 支取 200元 */
insert into transInfo(cardID,transType,transMoney)
values('1001 0001','支取',200); --------------- 查看结果 ---------------
select * from bank;
select * from transInfo;
go
2.2、触发器格式
create trigger [ schema_name. ] -- 触发器所属架构
trigger_name -- 触发器名称
on { table | view } -- 触发器的表或视图
[ with encryption ] -- 加密dml触发器定义(后面详解)
{ for | after }
/* after:只有在触发它的SQL语句执行成功后才能激发。
(只能对“表”定义after) */
{ insert,update,delete }
as
/* SQL语句... */
go --查看当前数据库中的所有触发器
select * from sys.triggers
2.3、Insert 触发器
------------------ insert 触发器 ------------------
use TriggerDatabase
go
if exists(select * from sysobjects
where name='trig_insert_transInfo')
drop trigger trig_insert_transInfo
go -- create trigger必须是批处理(go)的第一句 create trigger trig_insert_transInfo
on transInfo for insert
as
declare @_transType char(4), --定义变量
@_transMoney money,
@_cardID char(10),
@balance money --所剩余额 -- 从inserted临时表中获取记录值
select @_transType = transType,
@_transMoney = transMoney,
@_cardID = cardID
from inserted
if(@_transType = '支取')
update bank set currentMoney=currentMoney-@_transMoney
where cardID = @_cardID;
else
update bank set currentMoney=currentMoney+@_transMoney
where cardID = @_cardID; --显示交易金额
print '交易成功! 交易金额:'
+ convert(varchar(20),@_transMoney) --显示所剩余额
select @balance = currentMoney from bank
where cardId = @_cardID print '卡号:'+@_cardID
+ ' 余额:'+convert(varchar(20),@balance);
go ------------------ 测试触发器 ------------------
-- delete from transInfo
set nocount on --不显示T-SQL影响的记录行数 insert into transInfo(cardID,transType,transMoney)
values('1001 0001','支取',200);
insert into transInfo(cardID,transType,transMoney)
values('1001 0001','存入',10000);
--查看结果
select * from bank
select * from transInfo
复制代码
2.4、delete 触发器
/* 实现: 当清除'交易信息表'的数据时,
自动备份被清除的数据到backupTable表中
*/ ------------------ delete 触发器 ------------------
use TriggerDatabase
go if exists (select * from sysobjects
where name='trig_delete_transInfo')
drop trigger trig_delete_transInfo
go create trigger trig_delete_transInfo
on transInfo after delete -- for | after
as
print '开始备份数据,请稍后......'
-- 如果数据库中,不存在 backupTable 表
if not exists(select * from sysobjects
where name='backupTable')
select * into backupTable from deleted --deleted临时表
else
insert into backupTable select * from deleted print '备份成功,备份表 backupTable 中的数据为:'
select * from backupTable;
go ------------------ 测试触发器 ------------------
set nocount on delete from transInfo; --测试 --查看结果
select * from transInfo
select * from backupTable
2.5、update 触发器
------------------ update 触发器 ------------------
use TriggerDatabase
go if exists (select * from sysobjects
where name='trig_update_bank')
drop trigger trig_update_bank
go create trigger trig_update_bank
on bank for update --在bank表上创建update触发器
as
declare @beforeMoney money,
@afterMoney money,
@currentTrans money --当前交易金额 --从deleted临时表,获取交易前的余额
select @beforeMoney = currentMoney from deleted;
--从inserted临时表,获取交易后的余额
select @afterMoney = currentMoney from inserted; if abs(@afterMoney-@beforeMoney) > 2000
begin
print '当前交易金额为:' +
convert(varchar(20),abs(@afterMoney-@beforeMoney))
-- 自定义错误消息
raiserror('每次交易金额不能超过2000元,交易失败!',16,1) rollback transaction --回滚事务,撤销交易!
/* 注意:
触发器是一个特殊的事务单元
不需显示声明begin transaction
*/
end
go ------------------ 测试触发器 ------------------
set nocount on --测试1: 在 bank表触发 update触发器
update bank set currentMoney = currentMoney + 25000
where cardID = '1001 0001' --测试2: 通过 transInfo表的 trig_insert_transInfo触发器
-- 间接触发 bank表的 trig_update_bank触发器 insert into transInfo(cardID,transType,transMoney)
values('1001 0001','存入',10000); --查看结果
select * from bank
select * from transInfo
2.6、MSDN参考
     2.6.1、加密 dml触发器定义
      若要确保其他用户不能查看触发器定义,可以使用with encryption子句加密 dml 触发器。
      使用with encryption子句后,触发器定义即以无法读取的格式进行存储。
      触发器定义加密后,无法进行解密。且任何人都无法进行查看,包括触发器的所有者和系统管理员。
     2.6.2、update() 函数:
      可用于确定 insert或 update语句是否影响表中的特定列。
      无论何时为列赋值,该函数都将返回 true。
使用if update() 子句示例:
create table testTable(a int null, b int null)
go
create trigger my_trig
on testTable for insert
as
if update(b)
print '列b已被修改!'
go insert into testTable(b) values(123); -- drop table testTable
注意:  
      由于 delete 语句无法只对某列进行删除,
      因此不能将if update()子句应用于delete 语句。
     2.6.3、columns_updated() 函数:
      也可用于检查 insert或 update语句更新了表中的哪些列。
      此函数使用整数位掩码指定要测试的列。
使用columns_updated() 函数示例:
create table testTable2(a int null, b int null)
go create trigger my_trig2
on testTable2 for insert
as
if ( columns_updated() & 2 = 2 )
print '列b已被修改!'
go insert into testTable2(b) values(123); -- drop table testTable2
转载:http://www.cnblogs.com/xugang/archive/2010/02/20/1669619.html
浅谈 Sql Server 触发器的更多相关文章
- 【SqlServer系列】浅谈SQL Server事务与锁(上篇)
		
一 概述 在数据库方面,对于非DBA的程序员来说,事务与锁是一大难点,针对该难点,本篇文章视图采用图文的方式来与大家一起探讨. “浅谈SQL Server 事务与锁”这个专题共分两篇,上篇主讲事务及 ...
 - 浅谈SQL Server内部运行机制
		
对于已经很熟悉T-SQL的读者,或者对于较专业的DBA来说,逻辑的增删改查,或者较复杂的SQL语句,都是非常简单的,不存在任何挑战,不值得一提,那么,SQL的哪些方面是他们的挑战 或者软肋呢? 那就是 ...
 - 浅谈SQL Server数据内部表现形式
		
在上篇文章 浅谈SQL Server内部运行机制 中,与大家分享了SQL Server内部运行机制,通过上次的分享,相信大家已经能解决如下几个问题: 1.SQL Server 体系结构由哪几部分组成? ...
 - c#Winform程序调用app.config文件配置数据库连接字符串  SQL Server文章目录   浅谈SQL Server中统计对于查询的影响   有关索引的DMV  SQL Server中的执行引擎入门 【译】表变量和临时表的比较  对于表列数据类型选择的一点思考  SQL Server复制入门(一)----复制简介  操作系统中的进程与线程
		
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
 - 浅谈SQL Server事务与锁(上篇)
		
一 概述 在数据库方面,对于非DBA的程序员来说,事务与锁是一大难点,针对该难点,本篇文章试图采用图文的方式来与大家一起探讨. “浅谈SQL Server 事务与锁”这个专题共分两篇,上篇主讲事务及 ...
 - 浅谈SQL Server中的事务日志(一)----事务日志的物理和逻辑构架
		
简介 SQL Server中的事务日志无疑是SQL Server中最重要的部分之一.因为SQL SERVER利用事务日志来确保持久性(Durability)和事务回滚(Rollback).从而还部分确 ...
 - 浅谈SQL Server 对于内存的管理
		
简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...
 - (转)浅谈SQL Server 对于内存的管理
		
简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...
 - 浅谈SQL Server中的三种物理连接操作
		
简介 在SQL Server中,我们所常见的表与表之间的Inner Join,Outer Join都会被执行引擎根据所选的列,数据上是否有索引,所选数据的选择性转化为Loop Join,Merge J ...
 
随机推荐
- BigData:值得了解的十大数据发展趋势
			
当今,世界无时无刻不在发生着变化.对于技术领域而言,普遍存在的一个巨大变化就是为大数据(Big data)打开了大门,并应用大数据技相关技术来改善各行业的业务并促进经济的发展.目前,大数据的作用已经上 ...
 - aiohttp爬虫的模板,类的形式
			
import asyncio import aiohttp import async_timeout from lxml import html from timeit import default_ ...
 - 折腾VIM的C++缩进
			
自己是2014年的时候,开始学习VIM编辑器.记得当时把整个VIM入门手册几乎通读了一边,为其强大的功能和便捷的操作所折服. 今天再次捣鼓了以下VIM,只因为用VIM编辑C++的代码时,类中的publ ...
 - 数据结构与算法之排序(1)冒泡排序 ——in dart
			
最经典的入门排序算法,冒泡排序,dart语言实现.数组仍然采用随机生成的数组,使用dart内置的List 的generate方法,排序前后分别打印出数组,以观察效果. import 'dart:mat ...
 - 20155210潘滢昊 2016-2017-2 《Java程序设计》第4周学习总结
			
20155210 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 extends:单一继承. 抽象方法.抽象类: abstract:Java中有抽象方法的类一 ...
 - # 20155224 2016-2017-2《Java程序设计》课程总结
			
20155224 2016-2017-2<Java程序设计>课程总结 每周作业链接汇总 预备作业1:我所期望的师生关系 预备作业2:我的技能与C语言学习 预备作业3:Linux的初步学习, ...
 - 每天一个linux命令(1):ln 命令
			
每天一个linux命令(35):ln 命令 ln 是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在 每一 ...
 - 使用Python的BeautifulSoup 类库采集网页内容
			
BeautifulSoup 一个分析.处理DOM树的类库.可以做网络爬虫.模块简称bs4. 安装类库 easy_install beautifulsoup4 pip install beautiful ...
 - logstash处理@timestamp时区
			
input { stdin { } } filter { #ruby { # code => "event.set('timestamp', event.get('@timestamp ...
 - Mybatis JPA-集成方案+源码
			
2018-04-18 update 当前文章已过时,请访问代码仓库查看当前版本wiki. github https://github.com/cnsvili/mybatis-jpa gitee htt ...