1、什么是触发器
当数据库中发生数据操作语言 (DML) 事件时将调用 DML 触发器。DML 事件包括在指定表或视图中修改数据的 INSERT 语句、UPDATE 语句或 DELETE 语句。DML 触发器可以查询其他表,还可以包含复杂的 Transact-SQL 语句。将触发器和触发它的语句作为可在触发器内回滚的单个事务对待。如果检测到错误(例如,磁盘空间不足),则整个事务即自动回滚。
- 触发器是在对表进行插入、更新或删除操作时自动执行的存储过程
- 触发器通常用于强制业务规则
- 触发器是一种高级约束,可以定义比用CHECK 约束更为复杂的约束 :可执行复杂的SQL语句(if/while/case)、可引用其它表中的列
- 触发器定义在特定的表上,与表相关。
- 自动触发执行
- 不能直接调用
- 是一个事务(可回滚)
2、触发器的类型
Microsoft SQL Server 提供两种主要机制来强制使用业务规则和数据完整性:约束和触发器。触发器为特殊类型的存储过程,可在执行语言事件时自动生效。SQL Server 包括三种常规类型的触发器:DML 触发器、DDL 触发器和登录触发器。
下面讲解的是DML触发器:
分类:
- DELETE 触发器
- INSERT 触发器
- UPDATE 触发器
3、inserted 和deleted 表
- 系统自动在内存中创建deleted表或inserted表
- 只读,不允许修改;触发器执行完成后,自动删除。
- 临时保存了插入或更新后的记录行
- 可以从inserted表中检查插入的数据是否满足业务需求
- 如果不满足,则向用户报告错误消息,并回滚插入操作
- 临时保存了删除或更新前的记录行
- 可以从deleted表中检查被删除的数据是否满足业务需求
- 如果不满足,则向用户报告错误消息,并回滚插入操作
修改操作 |
inserted表 |
deleted表 |
增加(INSERT)记录 |
存放新增的记录 |
------ |
删除(DELETE)记录 |
----- |
存放被删除的记录 |
修改(UPDATE)记录 |
存放更新后的记录 |
存放更新前的记录 |
4、如何创建触发器
CREATE TRIGGER trigger_name
ON table_name
[WITH ENCRYPTION]
FOR [DELETE, INSERT, UPDATE]
AS
T-SQL语句
GO
WITH ENCRYPTION表示加密触发器定义的SQL文本
DELETE, INSERT, UPDATE指定触发器的类型
INSERT 触发器示例
- 在记录信息表上创建INSERT触发器
- 从inserted临时表中获取插入的数据行
- 根据电脑编号(PCId)修改电脑状态是否为使用状态,修改电脑状态。
/*创建insert触发器,在上网记录表recordInfo上创建插入触发器*/
create trigger tr_insert_recordInfo
on recordInfo
for insert
as
/*定义变量,用于临时存储插入的会员号、电脑编号和卡的编号*/
declare @cardId char(10)
declare @PCId int
declare @CardNumber char(10)
/*从inserted临时表中获取插入的记录行的信息,包括电脑的编号、卡的编号*/
select @PCId=PCId,@cardId=CardId from inserted
/*根据电脑编号修改电脑的使用状态*/
update PCInfo set PCUse=1 where PCId=@PCId
/*根据卡的编号查询会员号*/
select @CardNumber=CardNumber from cardinfo where CardId=@cardid
/*显示上机成功的信息*/
print '上机成功!会员号是:'+@CardNumber+'机器号是:'+convert(char(10),@PCId)
go
----插入测试数据,会员号为的上机
set nocount on --不显示sql语句影响的记录行数
declare @CardId int ---声明一个存储卡的编号的变量
---根据会员号查处卡的编号
select @cardId=cardid from cardinfo where cardNumber='c001'
---向recordInfo表中插入一条记录信息,卡的编号、电脑编号和上机时间
insert into recordInfo(cardId,PCId,beginTime) values(@cardId,1,getDate())
----查看结果
select * from recordInfo
select * from PCInfo
DELETE触发器示例
- 在记录表上创建DELETE触发器
- 被删除的数据可以从deleted表中获取
---创建delete触发器,在上网记录表RecordInfo上创建删除触发器
create trigger tr_delete_recordInfo
on recordInfo
for delete
as
if exists(select * from sysobjects where name='backRecordInfo')
----如果backrecordInfo表存在,就添加记录即可
insert into backRecordInfo select * from deleted
else
----创建backRecordInfo表,从deleted中获取被删除的数据
select * into backRecordInfo from deleted
print'backRecordInfo表备份数据成功,备份表中的数据为:'
select * from backRecordInfo
go
-------关键代码------
----测试delete触发器,删除数据
set nocount on
delete from recordInfo
---查看结果
print'记录表中的数据为:'
select * from recordInfo
UPDATE触发器示例
- 当一台电脑空闲时,我们需要修改当前电脑的使用状态为0(正常状态),当一台电脑正在使用时,我们需要修改当前电脑的使用状态为1(使用状态);当修改记录表时,要获取电脑的信息。
- 为记录表创建update触发器,这样就可以同时修改电脑的状态信息。
-------关键代码------
create trigger tr_update_recordInfo
on recordInfo
for update
as
declare @beforePCId int
declare @afterPCId int
select @beforePCId =PCId from deleted
select @afterPCId=PCID from inserted
---根据电脑编号修改使用状态-----
---根据以前使用的电脑编号把电脑的使用状态改为:
update PCInfo set PCUse=0 where PCId=@beforePCId
---根据现在使用的电脑编号把电脑的使用状态改为:
update PCInfo set PCUse=1 where PCId=@afterPCId
----显示电脑换机成功
print'换机成功!从'+convert(varchar(10),@beforePCId)+'号电脑换到'+convert(varchar(10),@afterPCId)+'号电脑'
go
/*测试update触发器,修改电脑编号*/
--显示更改前,记录表中的数据
print'更改前,记录表中的数据'
select * from recordInfo
--显示更改前,电脑表中的数据
print'更改前,电脑表中的数据'
select * from PCInfo
set nocount on
---把电脑号为1的改为2
update recordInfo set PCId=2 where PCId=1
---查看结果
print'更改后,记录表中的数据'
select * from recordInfo
print'更改后,电脑表中的数据'
select * from PCInfo
列级 UPDATE 触发器
-------关键代码------
---创建update触发器,在上网记录表recordInfo上创建修改(列)触发器
create trigger tr_updateColum_recordInfo
on recordInfo
for update
as
---检查是否修改了上机时间(beginTime)
if update(beginTime)
begin
print'修改失败!'
raiserror('安全警告:上机时间不能修改,由系统自动产生',16,1)
rollback transaction ----回滚操作,撤销操作
end
go
-------关键代码------
set nocount on
---把上机时间修改为-5
update recordInfo set beginTime='2010-6-5'
5、instead of触发器的使用
- instead of触发器的使用范围,instead of 触发器可以同时在数据表和视图中使用。通常在以下几种情况下,建议使用instead of触发器:
- 数据库里的数据禁止修改:例如电信部门的通话记录是不能修改的,一旦修改,则通话费用的计数将不准确。
- 有可能要回滚修改的SQL语句
- 在视图中使用触发器
- 用自己的方式去修改数据
instead of触发器的使用
---创建update触发器,在上网记录表recordInfo上创建修改(列)触发器
create trigger tr_updateColum1_recordInfo
on recordInfo
instead of insert
as
declare @cardbalance int --声明用于存储用户余额的变量
declare @CardId int --声明用于存储用户卡的编号的变量
declare @PCId int --声明用于存储电脑编号的变量
---inserted临时表中获取插入的记录行信息,包括电脑编号、卡的编号
select @cardId=cardId,@PCId=PCId from inserted
select @cardbalance=cardBalance from cardInfo where CardId=@CardId
print'您的余额为:'+convert(varchar(10),@cardBalance) ---打印余额信息
if(@cardBalance<2) ---判断余额多少,看能否正常上机
print'余额小于元,不能上机。请尽快充值!'
else
----根据电脑的编号修改电脑的使用状态更改为正在使用
update PCInfo set PCUse=1 where PCId=@PCId
----向recordInfo表插入上机记录
insert into recordInfo(cardId,PCId,beginTime)values(@CardId,@PCId,getdate())
print'上机成功'
-------关键代码------
set nocount on
declare @cardId int ---声明一个存储卡的编号的变量
---根据会员号查出卡的编号
select @cardId=cardId from cardInfo where cardNumber='c001'
----向recordInfo表中插入一条记录信息,卡的编号、电脑的编号和上机时间
insert into recordInfo(cardId,PCId,beginTime)values(@cardId,1,getdate())
select * from recordInfo
select * from PCInfo
instead of触发器的创建语法
create trigger trigger_name
on table_name
instead of insert
as
sql语句
6、使用触发器的优缺点
- 触发器可以强制比用check约束定义的约束更为复杂的约束。触发器也可以评估数据修改前后的表状态,并根据其差异采取对策。一张表中的多个同类触发器(insert、update或delete)允许采取多个不同的对策以响应同一条修改语句。
- 触发器的主要作用:实现由主键和外键所不能保证的复杂的参照完整性和数据的一致性。
- 触发器功能强大,轻松可靠地实现许多复杂的功能,为什么又要慎用呢。触发器本身没有错,但由于我们的滥用会造成数据库及应用程序的维护困难。在数据库操作中,我们可以通过关系、触发器、存储过程、应用程序等来实现数据操作。同时规则、约束、缺省值也是保证数据完整性的重要屏障。如果我们对触发器过分的依赖,势必影响数据库的结构,同时增加了维护的复杂程度。