T-SQL 触发器

一、概述

触发器(TRIGGER)是一种特殊类型的存储过程,包含了一组T-SQL语句,它的执行不是由程序调用,也不是手工启动,而是由事件触发的,在指定的表中的数据发生变化时自动生效,唤醒调用触发器以响应 INSERT、UPDATE 或 DELETE 语句,无需再执行增删改或存储过程的操作,让程序可以更加自动化。

二、类型

在这里插入图片描述

主要分为DML触发器和DDL触发器两大类,其实还包括登录触发器,也在下面简单介绍一下。

1.DML触发器

DML触发器是在数据库服务器中发生数据操作语言事件时执行的存储过程,也就是说,DML触发器在执行INSERT、DELETE、UPDATE语句时触发。根据触发的时机不同,DML触发器又分为AFTER触发器和INSTEAD OF触发器两种。
1.After 触发器是在记录已经修改完成后才会被激活执行,它主要用于记录变更后的处理或检查,一旦发现错误,也可以用 ROLLBACK TRANSACTION1语句来回滚本次操作。(创建时使用for或after作为关键字)

比如我们最常见的就是 当语句执行完成后,下面会自动弹出来的消息栏:“命令已成功完成”,“警告:”等等。
SQL Server 里并没有 before 触发器,那是 oracle 里的。

2.Instead of 触发器一般用于取代原本的操作,在记录变更之前发生,它不去执行原来的SQL语句,而去执行触发器中所定义的操作。(创建时使用instead of作为关键字)

After 触发器只能在数据表中使用,而 Instead of 触发器可以在数据表和视图中使用。很容易可以想到当我们执行update或其他操作时,可以通过加一个替代触发器,来取代原来的操作,防止错误发生。

3.幻表: 系统为每个DML触发器都定义了两个特殊的表,当DML触发器激发对INSERT,DELETE或UPDATE语句的响应时,这两个特殊的表被创建: INSERTED表和DELETEED表, 也称为幻表(魔表),结束后又自动删除。
3.1 INSERTED表包含插入在触发器表中的所有记录的拷贝,或修改后的记录值
3.2 DELETEED表包含了已从触发器表中被删除的所有记录,或修改前的记录值

个人理解两张表记录的就是新,旧数据,两张表之间是可以连接的。

2.DDL触发器

DDL触发器是在响应数据定义语言事件时执行的存储过程。DDL触发器一般用于执行数据库中的管理任务,如审核和规范数据库操作,防止对数据库架构、视图、表、存储过程等进行的某些修改。
DDL触发器并不响应INSERT、DELETE、UPDATE语句,它主要响应CREAT,ALTER,DROP,GRANT,DENY,REVOKE等语句。

DML和DDL的不同:
只有在完成T-SQL语句后才运行DDL触发器,DDL触发器无法作为替代触发器使用;
DDL触发器不会创建DELETEED表和INSERTED表,但是可以使用EVENTDATE函数捕获有关信息。

3.登录触发器

顾名思义,都是和登录相关。登录触发器2将为响应LOGON 事件而激发存储过程。 与 SQL Server实例建立用户会话时将引发此事件。 登录触发器将在登录的身份验证阶段完成之后且用户会话实际建立之前激发。 因此,来自触发器内部且通常将到达用户的所有消息(例如错误消息和来自 PRINT 语句的消息)会传送到 SQL Server 错误日志。 如果身份验证失败,将不激发登录触发器。
可以使用登录触发器来审核和控制服务器会话,例如通过跟踪登录活动、限制 SQL Server的登录名或限制特定登录名的会话数等等,都是很有意思的。

比如我们要是想限制一个名叫 user1 的用户在七点到八点登录,我们就可以简单的写成:

create login user1 with password = '123'
go
if exists(select * from sys.triggers where name='login_tri')
drop trigger login_tri on database  
go 
create trigger login_tri
on all server
for logon
as
begin
    if (
    	original_login() = 'user1' and datepart(hour,getdate()) between 7 and 8           
       )
    begin
        rollback;
    end;
end;

在这里插入图片描述

三、操作

1.创建触发器

create trigger trigger_name
on { object name }
{ instead of | after | for }
{ [insert][,][update][,][delete] }
as
{ sql_statement [ …n] }

trigger_name 触发器名称(自取)
object name 触发器针对的表(视图)名称
instead of | after | for 三选一
[insert][,][update][,][delete] 你希望做哪些事时会触发机关

2.修改触发器

create trigger trigger_name
on { object name }
{ instead of | after | for }
{ [insert][,][update][,][delete] }
as
{ sql_statement [ …n] }

不难发现,类似于创建触发器,只需要把上述语句中的 create 换成 alter 即可。

3.删除触发器

drop trigger trigger_name

支持同时删除多个触发器,英文逗号隔开。

4.查看触发器

  • 查看表中的触发器
    exec sp_helptrigger table_name
  • 查看触发器的定义版本
    exec sp_helptext ‘trigger_name’
  • 查看触发器的所有者和创建时间
    exec sp_help ‘trigger_name’

table_name是触发器所在表的名称,若不指定则列出所有的触发器。

5.启用和禁用触发器

前提是你有相应的权限,对于DML触发器一般就是至少要有权限可以对表或视图进行 alter 。

  • alter table table_name disable | enable trigger trigger_name;

禁用或启用某张表上的某个触发器

  • alter table table_name disable | enable trigger all;

禁用或启用某张表上的所有触发器

exec sp_MSforeachtable’alter table ? disable | enable trigger all’

禁用或启用所有表上的所有触发器

四、练习

1.数据库的创建

create database 练习
go 
use 练习
go

create table student(
	sno char(10) primary key,
	sname char(20) not null,
	ssex char(2) check (Ssex in ('男','女')) not null,
	sage tinyint not null,
)
go 

insert student (sno, sname, ssex,sage) values('G2016001','李素素','女',19)
insert student (sno, sname, ssex,sage) values('G2016002','朱萍','女',21)
insert student (sno, sname, ssex,sage) values('G2016003','叶家裕','男',28)
insert student (sno, sname, ssex,sage) values('G2016004','邓家如','女',20)
insert student (sno, sname, ssex,sage) values('G2016005','高晓','女',21)
insert student (sno, sname, ssex,sage) values('J2016001','杨华','男',20)
insert student (sno, sname, ssex,sage) values('J2016002','刘全珍','女',22)
insert student (sno, sname, ssex,sage) values('J2016003','王国','男',21)
insert student (sno, sname, ssex,sage) values('J2016004','孙荣','男',21)
insert student (sno, sname, ssex,sage) values('J2016005','胡娟','女',20)
insert student (sno, sname, ssex,sage) values('L2016001','张茂桦','男',22)
insert student (sno, sname, ssex,sage) values('L2016002','方杰','男',21)
insert student (sno, sname, ssex,sage) values('L2016003','刘可','女',20)
insert student (sno, sname, ssex,sage) values('L2016004','黄一秋','男',21)
insert student (sno, sname, ssex,sage) values('L2016005','唐治','男',20)
insert student (sno, sname, ssex,sage) values('L2016006','韩云','男',20)
insert student (sno, sname, ssex,sage) values('S2016001','徐川','男',20)
insert student (sno, sname, ssex,sage) values('S2016002','汤洪','男',21)
insert student (sno, sname, ssex,sage) values('S2016003','马秋婷','女',20)
insert student (sno, sname, ssex,sage) values('S2016004','周英','女',20)
insert student (sno, sname, ssex,sage) values('S2016005','曹林','男',21)
go

create table course(
	cno char(10) primary key,
	cname char(30) not null,
	ccredit tinyint check (ccredit>=1 and ccredit<=10) not null,
	XKLB char(5) check (XKLB = '选修' OR [XKLB]='必修') not null
 )
go

insert course(cno, cname, ccredit,xklb) values ('B001','数据库及应用',4,'必修')
insert course(cno, cname, ccredit,xklb) values ('B002','数据结构',4,'必修')
insert course(cno, cname, ccredit,xklb) values ('B003','程序设计思想',3,'必修')
insert course(cno, cname, ccredit,xklb) values ('B004','企业文化',3,'必修')
insert course(cno, cname, ccredit,xklb) values ('B005','会计学',3,'必修')
insert course(cno, cname, ccredit,xklb) values ('B006','高等数学',2,'必修')
insert course(cno, cname, ccredit,xklb) values ('B007','线性代数',3,'必修')
insert course(cno, cname, ccredit,xklb) values ('B008','大学英语',4,'必修')
insert course(cno, cname, ccredit,xklb) values ('B009','马克思主义哲学',4,'必修')
insert course(cno, cname, ccredit,xklb) values ('X001','大学生就业指导',1,'选修')
insert course(cno, cname, ccredit,xklb) values ('X002','大学生心理指导',1,'选修')
insert course(cno, cname, ccredit,xklb) values ('X003','大学生思想修养',1,'选修')
go

create table sc(
	sno char(10) not null,
	cno char(10) not null,
	grade numeric(3, 0)check (grade>=0 and grade<=100),
	primary key(sno,cno),
	foreign key(sno)references student(sno),
	foreign key(cno)references course(cno)
)
go

insert sc (sno,cno,grade) values ('G2016001','B004',95)
insert sc (sno,cno,grade) values ('G2016001','B008',88)
insert sc (sno,cno,grade) values ('J2016001','B001',90)
insert sc (sno,cno,grade) values ('J2016003','B001',85)
insert sc (sno,cno,grade) values ('J2016005','B003',87)
insert sc (sno,cno,grade) values ('J2016001','B002',91)
insert sc (sno,cno,grade) values ('J2016004','B008',50)
insert sc (sno,cno,grade) values ('J2016004','B002',85)
insert sc (sno,cno,grade) values ('G2016003','B005',86)
insert sc (sno,cno,grade) values ('G2016003','X001',91)
insert sc (sno,cno,grade) values ('L2016001','B007',80)
insert sc (sno,cno,grade) values ('L2016002','B007',92)
insert sc (sno,cno,grade) values ('L2016004','B007',83)
insert sc (sno,cno,grade) values ('L2016005','B006',78)
insert sc (sno,cno,grade) values ('L2016001','B006',48)
insert sc (sno,cno,grade) values ('L2016006','B008',90)
insert sc (sno,cno,grade) values ('S2016001','B008',85)
insert sc (sno,cno,grade) values ('S2016002','B008',79)
insert sc (sno,cno,grade) values ('S2016003','B008',80)
insert sc (sno,cno,grade) values ('S2016001','B006',90)
insert sc (sno,cno,grade) values ('S2016004','B006',86)
insert sc (sno,cno,grade) values ('S2016005','X003',92)
insert sc (sno,cno,grade) values ('S2016003','X003',88)
insert sc (sno,cno,grade) values ('G2016002','X002',NULL)
insert sc (sno,cno,grade) values ('G2016005','X003',NULL)
go

2.练习

最好在每次前面加一段语句,非必须,看自己。

--在目标数据库中进行操作
use database_name
go
-- 判断名为 trigger_name的触发器是否存在
if exists(select * from sys.triggers where name='trigger_name')
--删除名为 trigger_name的触发器
drop trigger trigger_DDL_Table on database  
go      
  • 在COURSE表上创建一个名为“AfterInsertTRI”的INSERT后触发器,判断插入的课程学分是否大于5,如果大于5,将其学分修改为5。
create trigger AfterInsertTRI on Course
after insert 
as
update Course
set Ccredit = 5 where Ccredit in (
	select Ccredit from Course where Ccredit > 5
)
  • 删除上题创建的“AfterInsertTRI”触发器,然后在COURSE表上创建一个名为“InsteadInsertTRI”的INSERT替代触发器,触发器判断插入的课程学分是否大于5,如果大于5,将学分修改为5再插入;如果学分小于等于5,按原插入数据插入
--这是用的后触发器,我写错了,没读题...
drop trigger InsteadInsertTRI
go--注意这个go,批处理结束后是必须要有的
create trigger InsteadInsertTRI on Course
for insert,update
as
update Course
set Ccredit = 5
from Course,inserted
where Course.Cno = inserted.Cno and inserted.Ccredit > 5
drop trigger AfterInsertTRI
go
create TRIGGER InsteadInsertTRI ON course
instead of INSERT 
as 
begin
    insert  course(cno,cname,ccredit,xklb)
    select cno,cname,ccredit,xklb from inserted
    update course set ccredit = 5 where ccredit > 5
end
  • 使用UPDATE触发器实现:当修改SC表的成绩时(每次只更新一行数据),如果修改后的成绩小于等于修改以前的,则不允许修改,并提示“修改后成绩小于等于修改前成绩,修改失败!”,否则提示“修改成功!”。
create trigger UPDATETRG on sc
for update 
as
if update(grade) 
begin
	if  exists(select * from inserted join deleted on inserted.sno=deleted.sno 
where inserted.grade < deleted.grade)
	begin
		print '修改后成绩小于等于修改前成绩,修改失败!'
		rollback transaction
	end
	else
		print'修改成功!'
end


  1. rollback
    回滚,类似于撤销。 ↩︎

  2. 登录触发器 ↩︎

猜你喜欢

转载自blog.csdn.net/m0_51086313/article/details/111620961