SQL触发器实例
定义 何为触发器?在SQL Server里面也就是对某一个表的一定的操作触发某种条件从而执行的一段程序触发器是一个特殊的存储过程
常见的触发器有三种分别应用于Insert Update Delete 事件
我为什么要使用触发器?比如这么两个表
复制代码代码如下:
Create Table Student( 学生表
StudentID int primary key 学号
)
Create Table BorrowRecord( 学生借书记录表
BorrowRecord int identity() 流水号
StudentID int 学号
BorrowDate datetime 借出时间
ReturnDAte Datetime 归还时间
)
用到的功能有:
如果我更改了学生的学号我希望他的借书记录仍然与这个学生相关(也就是同时更改借书记录表的学号);
如果该学生已经毕业我希望删除他的学号的同时也删除它的借书记录
等等
这时候可以用到触发器对于创建一个Update触发器
复制代码代码如下:
Create Trigger truStudent
On Student 在Student表中创建触发器
for Update 为什么事件触发
As 事件触发后所要做的事情
if Update(StudentID)
begin
Update BorrowRecord
Set StudentID=iStudentID
From BorrowRecord br Deleted d Inserted i Deleted和Inserted临时表
Where brStudentID=dStudentID
end
理解触发器里面的两个临时的表Deleted Inserted 注意Deleted 与Inserted分别表示触发事件的表“旧的一条记录”和“新的一条记录”
一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息分别是
虚拟表Inserted 虚拟表Deleted
在表记录新增时 存放新增的记录 不存储记录
修改时 存放用来更新的新记录 存放更新前的记录
删除时 不存储记录 存放被删除的记录
一个Update 的过程可以看作为生成新的记录到Inserted表复制旧的记录到Deleted表然后删除Student记录并写入新纪录
对于创建一个Delete触发器
复制代码代码如下:
Create trigger trdStudent
On Student
for Delete
As
Delete BorrowRecord
From BorrowRecord br Delted d
Where brStudentID=dStudentID
从这两个例子我们可以看到了触发器的关键A个临时的表B触发机制
SQL触发器实例
复制代码代码如下:
/*
建立虚拟测试环境包含表[卷烟库存表]表[卷烟销售表]
请大家注意跟蹤这两个表的数据体会触发器到底执行了什么业务逻辑对数据有什么影响
为了能更清晰的表述触发器的作用表结构存在数据冗余且不符合第三范式这里特此说明
*/
USE Master
GO
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = U AND NAME = 卷烟库存表)
DROP TABLE 卷烟库存表
GO
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = U AND NAME = 卷烟销售表)
DROP TABLE 卷烟销售表
GO
业务规则销售金额 = 销售数量 * 销售单价 业务规则
CREATE TABLE 卷烟销售表
(
卷烟品牌 VARCHAR() PRIMARY KEY NOT NULL
购货商 VARCHAR() NULL
销售数量 INT NULL
销售单价 MONEY NULL
销售金额 MONEY NULL
)
GO
业务规则库存金额 = 库存数量 * 库存单价 业务规则
CREATE TABLE 卷烟库存表
(
卷烟品牌 VARCHAR() PRIMARY KEY NOT NULL
库存数量 INT NULL
库存单价 MONEY NULL
库存金额 MONEY NULL
)
GO
创建触发器示例
/*
创建触发器[T_INSERT_卷烟库存表]这个触发器较简单
说明 每当[卷烟库存表]发生 INSERT 动作则引发该触发器
触发器功能 强制执行业务规则保证插入的数据中库存金额 = 库存数量 * 库存单价
注意 [INSERTED][DELETED]为系统表不可创建修改删除但可以调用
重要 这两个系统表的结构同插入数据的表的结构
*/
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = TR AND NAME = T_INSERT_卷烟库存表)
DROP TRIGGER T_INSERT_卷烟库存表
GO
CREATE TRIGGER T_INSERT_卷烟库存表
ON 卷烟库存表
FOR INSERT
AS
提交事务处理
BEGIN TRANSACTION
强制执行下列语句保证业务规则
UPDATE 卷烟库存表
SET 库存金额 = 库存数量 * 库存单价
WHERE 卷烟品牌 IN (SELECT 卷烟品牌 from INSERTED)
COMMIT TRANSACTION
GO
/*
针对[卷烟库存表]插入测试数据
注意第一条数据(红塔山新势力)中的数据符合业务规则
第二条数据(红塔山人为峰)中[库存金额]空不符合业务规则
第三条数据(云南映像)中[库存金额]不等于[库存数量]乘以[库存单价]不符合业务规则
第四条数据库存数量为
请注意在插入数据后检查[卷烟库存表]中的数据是否 库存金额 = 库存数量 * 库存单价
*/
INSERT INTO 卷烟库存表(卷烟品牌库存数量库存单价库存金额)
SELECT 红塔山新势力 UNION ALL
SELECT 红塔山人为峰NULL UNION ALL
SELECT 云南映像 UNION ALL
SELECT 玉溪
GO
查询数据
SELECT * FROM 卷烟库存表
GO
/*
结果集
RecordId 卷烟品牌 库存数量 库存单价 库存金额
红塔山新势力
红塔山人为峰
云南映像
玉溪
(所影响的行数为 行)
*/
触发器示例
/*
创建触发器[T_INSERT_卷烟销售表]该触发器较复杂
说明: 每当[卷烟库存表]发生 INSERT 动作则引发该触发器
触发器功能 实现业务规则
业务规则: 如果销售的卷烟品牌不存在库存或者库存为零则返回错误
否则则自动减少[卷烟库存表]中对应品牌卷烟的库存数量和库存金额
*/
IF EXISTS (SELECT NAME FROM SYSOBJECTS WHERE XTYPE = TR AND NAME = T_INSERT_卷烟销售表)
DROP TRIGGER T_INSERT_卷烟销售表
GO
CREATE TRIGGER T_INSERT_卷烟销售表
ON 卷烟销售表
FOR INSERT
AS
BEGIN TRANSACTION
检查数据的合法性销售的卷烟是否有库存或者库存是否大于零
IF NOT EXISTS (
SELECT 库存数量
FROM 卷烟库存表
WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED)
)
BEGIN
返回错误提示
RAISERROR(错误!该卷烟不存在库存不能销售)
回滚事务
ROLLBACK
RETURN
END
IF EXISTS (
SELECT 库存数量
FROM 卷烟库存表
WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED) AND
库存数量 <=
)
BEGIN
返回错误提示
RAISERROR(错误!该卷烟库存小于等于不能销售)
回滚事务
ROLLBACK
RETURN
END
对合法的数据进行处理
强制执行下列语句保证业务规则
UPDATE 卷烟销售表
SET 销售金额 = 销售数量 * 销售单价
WHERE 卷烟品牌 IN (SELECT 卷烟品牌 FROM INSERTED)
DECLARE @卷烟品牌 VARCHAR()
SET @卷烟品牌 = (SELECT 卷烟品牌 FROM INSERTED)
DECLARE @销售数量 MONEY
SET @销售数量 = (SELECT 销售数量 FROM INSERTED)
UPDATE 卷烟库存表
SET 库存数量 = 库存数量 @销售数量
库存金额 = (库存数量 @销售数量)*库存单价
WHERE 卷烟品牌 = @卷烟品牌
COMMIT TRANSACTION
GO
请大家自行跟蹤[卷烟库存表]和[卷烟销售表]的数据变化
针对[卷烟销售表]插入第一条测试数据该数据是正常的
INSERT INTO 卷烟销售表(卷烟品牌购货商销售数量销售单价销售金额)
SELECT 红塔山新势力某购货商
GO
针对[卷烟销售表]插入第二条测试数据该数据 销售金额 不等于 销售单价 * 销售数量
触发器将自动更正数据使 销售金额 等于 销售单价 * 销售数量
INSERT INTO 卷烟销售表(卷烟品牌购货商销售数量销售单价销售金额)
SELECT 红塔山人为峰某购货商
GO
针对[卷烟销售表]插入第三条测试数据该数据中的卷烟品牌在 卷烟库存表中找不到对应
触发器将报错
INSERT INTO 卷烟销售表(卷烟品牌购货商销售数量销售单价销售金额)
SELECT 红河V某购货商
GO
/*
结果集
服务器: 消息 级别 状态 过程 T_INSERT_卷烟销售表行
错误!该卷烟不存在库存不能销售
*/
针对[卷烟销售表]插入第三条测试数据该数据中的卷烟品牌在 卷烟库存表中库存为
触发器将报错
INSERT INTO 卷烟销售表(卷烟品牌购货商销售数量销售单价销售金额)
SELECT 玉溪某购货商
GO
/*
结果集
服务器: 消息 级别 状态 过程 T_INSERT_卷烟销售表行
错误!该卷烟库存小于等于不能销售
*/
查询数据
SELECT * FROM 卷烟库存表
SELECT * FROM 卷烟销售表
GO
/*
补充
本示例主要通过一个简单的业务规则实现来进行触发器使用的说明具体的要根据需要灵活处理
关于触发器要理解并运用好 INSERTED DELETED 两个系统表
本示例创建的触发器都是 FOR INSERT 具体的语法可参考
Trigger语法
复制代码代码如下:
CREATE TRIGGER trigger_name
ON { table | view }
[ WITH ENCRYPTION ] 用于加密触发器
{
{ { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ ] [ UPDATE ] }
[ WITH APPEND ]
[ NOT FOR REPLICATION ]
AS
[ { IF UPDATE ( column )
[ { AND | OR } UPDATE ( column ) ]
[ n ]
| IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )
{ comparison_operator } column_bitmask [ n ]
} ]
sql_statement [ n ]
}
}
关于触发器还应该注意
()DELETE 触发器不能捕获 TRUNCATE TABLE 语句
()触发器中不允许以下 TransactSQL 语句
ALTER DATABASE CREATE DATABASE DISK INIT
DISK RESIZE DROP DATABASE LOAD DATABASE
LOAD LOG RECONFIGURE RESTORE DATABASE
RESTORE LOG
()触发器最多可以嵌套 层
*/
修改触发器
实质上是将 CREATE TRIGGER 修改为 ALTER TRIGGER 即可
删除触发器
DROP TRIGGER xxx
GO
删除测试环境
DROP TABLE 卷烟库存表
GO
DROP TABLE 卷烟销售表
GO
DROP TRIGGER T_INSERT_卷烟库存表
GO
DROP TRIGGER T_INSERT_卷烟销售表
GO
##################################################################
触发器的基础知识和例子
create trigger tr_name
on table/view
{for | after | instead of } [update][][insert][][delete]
[with encryption]
as {batch | if update (col_name) [{and|or} update (col_name)] }
说明
tr_name 触发器名称
on table/view 触发器所作用的表一个触发器只能作用于一个表
for 和after 同义
after 与instead of :sql 新增项目afrer 与 instead of 的区别
After
在触发事件发生以后才被激活只可以建立在表上
Instead of
代替了相应的触发事件而被执行既可以建立在表上也可以建立在视图上
insertupdatedelete激活触发器的三种操作可以同时执行也可选其一
if update (col_name)表明所作的操作对指定列是否有影响有影响则激活触发器此外因为delete 操作只对行有影响
所以如果使用delete操作就不能用这条语句了(虽然使用也不出错但是不能激活触发器没意义)
触发器执行时用到的两个特殊表deleted inserted
deleted 和inserted 可以说是一种特殊的临时表是在进行激活触发器时由系统自动生成的其结构与触发器作用的表结构是一
样的只是存放 的数据有差异
续
下面表格说明deleted 与inserted 数据的差异
deleted 与inserted 数据的差异
Inserted
存放进行insert和update 操作后的数据
Deleted
存放进行delete 和update操作前的数据
注意update 操作相当于先进行delete 再进行insert 所以在进行update操作时修改前的数据拷贝一条到deleted 表中修改后
的数据在存到触发器作用的表的同时也同时生成一条拷贝到insered表中