SQL SERVER Trigger 触发器
定义
触发器【trigger】是一种特殊的存储过程,它在表的数据变化时发生作用。触发器可以维护数据完整性
触发器的作用就是保证参照完整性和数据的一致性
重点 实际开发经常用
ML触发器分为:
1、after触发器(之后触发)
a、insert触发器
b、update触发器
c、delete触发器
2、instead of触发器(之前触发)
after触发器要求只有执行某一操作(insert、update、delete)之后触发器才能被触发,且只能定义在表上。
而instead of触发器表示并不执行其定义的操作(insert、update、delete)而仅是执行触发器本身,其优先级高于触发语句的执行。
创建触发器 语法
CREATE TRIGGER trigger_name
ON table_name | View_name
[WITH ENCRYPTION]
FOR [DELETE, INSERT, UPDATE] | instead of [DELETE, INSERT, UPDATE]
[NO FOR REPLICATION]
AS
[ { IF UPDATE ( column )
[ { AND | OR } UPDATE ( column ) ]
[ …n ]
| IF ( COLUMNS_UPDATED ( ) updated_bitmask )
column_bitmask [ …n ]
} ]
T-SQL语句
GO
FOR: 表示为AFTER触发器,且触发器**仅能在表上创建**
INSTEASD OF:指定触发器为INSTEAD OF触发器
delete 、insert、update:指明执行那种操作,将激活触发器,至少要包含3种操作类型的一种,也可以是3种操作语句的任意组合。其中三者的顺序不受限制,且各选项要用逗号隔开
NO FOR REPLICATION:告诉DMBMS,当复制表时,触发器不能被执行
IF UPDATE column :用来测定对某一确定列是insert操作haisupdate操作,如果要测试insert还是update操作的列多于一列,可用and或or 逻辑连接向if update 子句添加所希望的附加列名
IF COLUMNS_UPDATED ( ):仅在insert和update类型的触发器中使用,检查列是被更新还是被插入
IF UPDATED(column)判断一个字段是否更新,而 IF COLUMNS_updated可以判断多个字段
注意事项
每个表最多只能有一个INSTEAD OF(insert 、update、delete)触发器,然而可以为每个表创建多个视图,对每个视图都可以有不同的 instead of 触发器
删除触发器
DROP TRIGGER trigger_name[,n]
Case 1 Insert 触发器
说明:为STUDENT表触发器S_insert,当向STUDENT表中插入数据时,要求学号必须以‘99’开头,且课程号CNO必须在COURSE表中,否则取消插入操作。代码如下:
create trigger s_insert
on STUDENT
for insert
as
declare @s_no varchar(4),@s_cno int
select @s_no=SNO,@s_cno=CNO from inserted
if (left(@s_no,2)!='99')
begin
ROLLBACK TRANSACTION
RAISERROR('输入的学号:%s不是99级的学生,请确认后重新录入!',16,1,@s_no)
end
if (@s_cno not in (select cno from COURSE))
begin
ROLLBACK TRANSACTION
RAISERROR('输入的课程号:%d在COURSE表中不存在,请确认后重新录入!',16,1,@s_cno)
end
注意
可用在一个表上创建多个触发器,数据库把一个表中所有触发器都看作同一个事物的一部分。因此只要其中一个触发器执行了ROLLBACK TRANSACTION语句,那么所有的操作(与该insert语句有关)都将取消
case 2 delete 触发器
CREATE TRIGGER S_delete
ON STUDENT
FOR delete
AS
declare @rowcount int
select @rowcount=@@ROWCOUNT
if @rowcount>1
begin
ROLLBACK TRANSACTION
RAISERROR('当前要删除的记录数为%d,一次只允许删除一行记录',16,1,@rowcount)
end
declare @S_dname varchar(16),@S_cno int
select @S_dname=DNAME, @S_cno=CNO from deleted
if(@S_cno in (select CNO from Teacher where DNAME=@S_dname))
BEGIN
ROLLBACK TRANSACTION
raiserror('删除记录的课程为本系即%s系教师所开设,不允删除!',16,1,@S_cno)
END
GO
注意
在SQL server 、mysql 中执行 TRUNCATE TABLE 语句,从表中删除所有的行时,不会触发DELETE触发器
case 3 update 触发器
特定的表上执行update 语句时,会触发update触发器。update 操作包括两个部分:
1、先及那个需要更新的内容从表中删除 deleted
2、然后插入新值 inserted
因此update触发器同时涉及到删除表和插入表
为了COURSE表创建update触发器C_update,当course表的CNO和CNAME (第1列和第2列)被更新时,触发器给出提示信息,该两列不能被更新,并回滚事物。其余的列(第3、4、5列)被更新时,触发器将更新前后的数据写入C_Upinfo表中。代码如下:
---创建跟踪信息表C_UpInfo
create table C_UpInfo
(
Opre_Time smalldatetime,
Date_Type CHAR(3).
CNO int,
CNAME char(30),
CTIME int,
SCOUNT int,
CTEST smalldatetime
)
-----创建UPDATE触发器 C_update
create trigger C_update
on COURSE
for UPDATE
as
if (COLUMNS_UPDATE()&>3) ---第1列或第二列被更新 11 转换10进制 3 & 指定多列中某一列更新 = 指定多列全部更新
begin
ROLLBACK TRANSACTION
RAISERROR('COURSE表的CNO和CNAME列中的数据不允许被更新!',16,1)
end
if (COLUMNS_UPDATE()&28>0)---第3或4或5被更新 00111 再从右到左 11100 转换十进制 28
begin
--将DELETED表【存放COURSE旧的数据】中存放的记录即更新前的记录存如UpInfo 表
insert into UpInfo(Oper_Time,Date_Type,CNO,CNAME,CTIME,SCOUNT,CTEST)
select GETDATE(),'OLD',del.CNO,del.CNAME,del.CTIME,del.SCOUNT,del.CTEST from deleted as del
---将inserted 表中存放的记录,即更新后的记录存入UpInfo
insert into UpInfo(Oper_Time,Date_Type,CNO,CNAME,CTIME,SCOUNT,CTEST)
select GETDATE(),'NEW',ins.CNO,ins.CNAME,ins.CTIME,ins.SCOUNT,ins.CTEST from inserted as ins
end
INSTEAD OF 触发器
INSTEAD OF 触发器的优点是使不能被更新给的视图支持更新操作。为了提高查询性能,视图通常来自多个表的结果集,基于多表视图不能被更新,而通过INSTEAD OF 触发器则可用实现这个功能,
---创建表 Stu97、Stu98、Stu99
create table Stu97
(
Sno char(5),
Sname Char(8),
Age int
)
create table Stu97
(
Sno char(5),
Sname Char(8),
Age int
)
create table Stu97
(
Sno char(5),
Sname Char(8),
Age int
)
---------创建视图Stu_View
create view Stu_View
as
select * from Stu97
union all
select * from Stu98
union all
select * from Stu99
----为视图Stu_View 创建INSTEAD OF 触发器 Stu_Instead
create trigger Stu_Instead
on Stu_View
INSTEAD OF INSERT
as
begin
declare @S_NO char(2)
--该变量用于存放插入数据的学号Sno的前2位,以判断插入记录属于哪张表
select @S_NO=substring(SNO,2) from inserted
if @S_NO-'97' --由学号判断该学生属于97级学生,记录插入Stu97表
begin
insert into Stu97 select Sno,Sname,Age from INSERTED
return
end
if @S_NO-'98' --由学号判断该学生属于97级学生,记录插入Stu98表
begin
insert into Stu97 select Sno,Sname,Age from INSERTED
return
end
if @S_NO-'99' --由学号判断该学生属于97级学生,记录插入Stu99表
begin
insert into Stu97 select Sno,Sname,Age from INSERTED
return
end
else
begin
ROLLBACK TRANSACTION
RAISERROR('插入记录的学号信息不正确,请确认97级、98级、99级学生的学号!',16,1)
end
end
嵌套触发器
当某一触发器执行时,能够触发另外一个触发器,这种情况称之触发器嵌套。在执行过程中,如果一个触发器修改某个表,而这个表已经有其它触发器,这时就使用了嵌套触发器。在SQL SERVER中,触发器嵌套至32层。如果不需要嵌套触发器,可以通过sp_configure 选项来进行设置
Oracle 数据库不支持嵌套触发器
案例
create trigger A_delete
on Author
for delete
as
declare @rowcount int
select @rowcount=@@ROWCOUNT
----记录删除操作所涉及的行数
if @rowcount>1
begin
ROLLBACK TRANSACTION
RAISERROR('当前要删除的记录数%d,一次只允许删除一行记录!',16,1,@rowcount)
end
else
begin
declare @Author char(8)
select @Author=Author from deleted ---记录删除记录的Author列信息
Delete Book where Author=@Author
end
----为Book表创建 delete 触发器 B_delete
create trigger B_delete
on Book
for delete
as
declare @rowcount int
select @rowcount=@@ROWCOUNT
if @rowcount>1
begin
ROLLBACK TRANSACTION
RAISERROR('当前要删除的记录数为%d,一次只允许删除一行记录!',16,1,@rowcount)
end
else
print 'Author表和Book表中相应的数据均被删除'
递归触发器
即由 trigger1 触发 trigger2, trigger2 又可用触发 trigger3,。。。。,而如果触发器triggern又触发trigger1,这就形成了递归触发器。
递归触发器分两种
1、间接递归
2、直接递归
假如有表Table1 、 Table2,在Table1、Table2上分别有触发器Trigger1、Trigger2
- 间接递归 对Table1操作触发Trigger1,Trigger1对Table2操作从而触发Trigger2,Trigger2对Table1操作从而再次触发Trigger1,。。。。
- 间接递归 对Table1操作从而触发Trigger1,Trigger1对Table1操作从而再次触发Trigger1,。。。。
注意
在默认情况下,SQL SERVER 是禁止直接递归的,要使SQL SERVER支持直接递归,可采用如下两种方式进行设置
1、通过系统存储过程sp_dboption 进行设置
语法:
sp_dboption ‘dbname’,‘Recursive Triggers’,true
dbname:数据库名
False:则禁止数据库直接递归
2、通Enterprise Manager进行设置
查看触发器
1、sp_help
2、sp_helptext 查看表、视图、存储过程、触发器等以文本信息展现,即触发器的创建语句。
3、sp_depends 能够查看指定触发器所引用的表或指定的表涉及到的所有触发器
sp_depends Table1 — 查看Table1表涉及到的所有触发器
更多推荐
所有评论(0)