主要内容参考:
https://blog.csdn.net/KingCruel/article/details/106292310
https://blog.csdn.net/qq_36330228/article/details/90582493
触发器:
触发器,可理解为一种特殊的存储过程。是一个特殊的事务(在执行过程中,可执行一些检查或设置条件,不满足时,可回滚操作)
存储过程,通过存储过程名称来调用执行;触发器,通过事件触发--自动调用执行;
触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,
它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。触发器经常用于加强数据的完整性约束和业务规则等。
触发器分类:
- DDL触发器:(数据定义语言,Data Definition Language) 例如,create\alter\drop
- DML触发器:(数据操作语言,Data Manipulation Language) 例如,insert\update\delete
- 操作完成后触发器:insert\update\delete
- insted of 触发器:只执行触发器本身,不执行其他定义的操作(insert\update\delete)
触发器语句中使用了两种特殊的表:deleted 表和 inserted 表
DML触发器执行时,系统内存会自动生成deleted表或inserted表,执行结束会自动消失。
SQL Server 有3类触发器【触发器是在对应表的下面】
Sql server中这三类触发器总是在执行操作语句后才被自动调用
- Insert触发器,使用到inserted表——Insert:向数据表插入数据时,调用insert触发器
- Update触发器,使用到deleted表和inserted表——Update:更新数据时调用update触发器
- Delete触发器,使用到deleted表——Delete:删除数据时执行Delete触发器
inserted delated:插入表、删除表; 逻辑表也是虚表,系统在内存中创建,不会存储到数据库中,只读的,读取但不能修改数据; 结构和操作的表相同; 在触发器执行过程中存在,且可以访问,触发器工作完成之后,这两张表会自动删除; Deleted 表用于存储 DELETE 和 UPDATE 语句所影响的行的复本 |
||
对表的操作 | inserted表 | deleted表 |
insert | 存放插入的数据 | 无数据 |
update | 存放更新后的数据--新数据 | 存放更新前的数据--老数据 |
delete | 无数据 | 存放被删除的数据 |
查询所有触发器:select * from sysobjects where xtype='TR'
执行已存在触发器:exec sp_helptext 'trrigername'
工作原理:
After(同for):先执行增、删、改、操作,再激活触发器操作。
Instead of:在执行增、删、改、操作之前,先激活触发器。
触发器优点:
1.强化约束:强制复杂业务的规则和要求,能实现比check语句更为复杂的约束。
2.跟踪变化:触发器可以侦测数据库内的操作,从而禁止数据库中未经许可的更新和变化。
3.级联运行:侦测数据库内的操作时,可自动地级联影响整个数据库的各项内容。
4.嵌套调用:触发器可以调用一个或多个存储过程。触发器最多可以嵌套32层。
触发器缺点:
1. 可移植性差。
2.占用服务器资源,给服务器造成压力。
3.执行速度主要取决于数据库服务器的性能与触发器代码的复杂程度。
4.嵌套调用一旦出现问题,排错困难,而且数据容易造成不一致,后期维护不方便。
触发器使用建议:
1.尽量避免在触发器中执行耗时操作,因为触发器会与SQL语句认为在同一事务中,事务不结束,就无法释放锁。
2.避免在触发器中做复杂操作,影响触发器性能的因素比较多(Eg:产品版本,所使用的架构等),要想编写高效的触发器考虑因素比较多,编写高性能触发器还是很难的。
3.触发器编写时注意多行触发时的处理。(一般不建议使用游标)
脚本语法:
create trigger trigger_TableName_Insert on TableName for insert -- 也可以写成 after insert as --TSQL list 。。。。。。 go
1.Insert触发器示例: 修改时Create 修改为 alter
在向目标表中插入数据后,会触发该表的Insert 触发器,系统自动在内存中创建inserted表; 下面的demo中对Age加了判断,如果不满足判断数据会进行回滚,插入的数据操作会失败。
create database DBTEST go use DBTEST go --创建学生表 create table student( student_id int identity(1,1) primary key, student_name varchar(10), student_age int, student_sex varchar(2) ); --插入学生数据 insert into student (student_name,student_age,student_sex) values ('张三',23,'男'), ('李四',18,'女'), ('王五',32,'男') select * from student create trigger trigger_student_Insert on student for insert -- 也可以写成 after insert as --TSQL list declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex +'新增数据成功!!!' go --insert 触发器 触发测试 insert into student (student_name,student_age,student_sex)values('坤坤',18,'男')
Insert 触发器示例: 修改时Create 修改为 alter
--Insert 触发器 Create TRIGGER [dbo].[Trigger_Insert] ON [dbo].[Person] AFTER INSERT AS BEGIN SET NOCOUNT ON; Declare @age int; Select @age=Age From inserted --如果年龄小于150正常插入,否则数据回滚 IF(@age<150) Begin Insert into PersonLog(PersonID, Name, Age, AddDate) Select ID, Name, Age, AddDate From inserted End ELSE Begin print('年龄应小于150') rollback transaction --数据回滚 END END
2.Update 触发器:deleted更改前——老数据;inserted修改后——新数据(Update 触发器,即 删除老数据,新增新数据)
在向目标表中更新数据后,会触发该表的Update 触发器,系统自动在内存中创建deleted表和inserted表,deleted表存放的是更新前的数据,inserted表存放的是更新的数据。
update 触发器——是删除 + 新增的操作
--Update 触发器 Create TRIGGER [dbo].[Trigger_Update] ON [dbo].[Person] AFTER UPDATE AS BEGIN SET NOCOUNT ON; --这里是先删除后插入,存在一张临时表deleted Insert Into PersonLog(PersonID, Name, Age, AddDate, UpdateDate) Select ID, Name, Age, AddDate, UpdateDate From inserted END
示例二:
--update 触发器 create trigger trigger_student_Update on student for Update -- 也可以写成 after Update as --TSQL list , 打印修改前、修改后的数据 declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) --deleted表用于存放,修改前数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from deleted print '修改前数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --inserted表用于存放,修改后数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print '修改后数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --其他操作 insert into student (student_name,student_age,student_sex)values('kunkun',18,'男') go --update 触发器,触发测试 update student set student_name='王麻子' where student_name='王五'
注:在实际工作中,在使用触发器时,在更改数据时,可将原始数据和新数据备份至不同的表,以备不时之需
inserted和deleted表中的数据是只读,不可操作
3.delete触发器
在向目标表中删除数据后,会触发该表的Delete 触发器,系统自动在内存中创建deleted表,deleted表存放的是删除的数据。
--Delete 触发器 Create TRIGGER [dbo].[Trigger_Delete] ON [dbo].[Person] AFTER DELETE AS BEGIN SET NOCOUNT ON; Insert Into PersonLog(PersonID, Name, Age, AddDate, UpdateDate, DeleteDate) Select ID, Name, Age, AddDate, UpdateDate, GETDATE() From deleted END
示例二:
--创建学生表数据备份表 if(OBJECT_ID('studentbak','U')is null) --is not null create table studentbak ( student_id int identity(1,1) primary key, student_name varchar(10), student_age int, student_sex varchar(2) ); ----------------------------------003delete 触发器 start---------------------------------- create trigger trigger_student_Delete on student for delete -- 也可以写成 after delete as --TSQL list , 打印修改前、修改后的数据 declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) --deleted表存放修改前的老数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from deleted print '删除的数据为:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --其他操作 insert into student (student_name,student_age,student_sex)values('yangyang',18,'女') --可备份至其他备份表中(目标表存在) insert into studentbak(student_name,student_age,student_sex)values(@student_name,@student_age,@student_sex) --目标表不存在 --select * from into studentbak from deleted go --delete 触发器,触发测试 delete from student where student_name='王麻子' go
4.instead of insert
- 不执行定义操作,执行的是触发器本身的操作
- 发生在插入操作之前
--after insert:在触发器中,标识列已经自动生成,有值
--instead of insert:在触发器中,标识列没有生成,没有值,执行完成,插入的数据不能正常插入
应用场景:在执行操作之前,进行部分检查之类的操作,再执行插入或其他操作(需将对应操作写入触发器)
----------------------------------004 instead of insert触发器 start------------------------------------ create trigger trigger_student_InsteadOfInsert on student instead of insert as declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print '要添加的数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex go --instead of insert 触发触发器,发生在插入操作之前 insert into student (student_name,student_age,student_sex)values('坤坤2',18,'男') select * from student --after insert:在触发器中,标识列已经自动生成,有值 --instead of insert:在触发器中,标识列没有生成,没有值,执行完成,插入的数据不能正常插入 ----------------------------------004 instead of insert触发器 end--------------------------------------
完整脚本如下所示:
create database DBTEST go use DBTEST go --创建学生表 create table student( student_id int identity(1,1) primary key, student_name varchar(10), student_age int, student_sex varchar(2) ); --插入学生数据 insert into student (student_name,student_age,student_sex) values ('张三',23,'男'), ('李四',18,'女'), ('王五',32,'男') select * from student ----------------------------------001insert 触发器 start---------------------------------- create trigger trigger_student_Insert on student for insert -- 也可以写成 after insert as --TSQL list declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex +'新增数据成功!!!' go --insert 触发触发器 insert into student (student_name,student_age,student_sex)values('坤坤',18,'男') select * from student ----------------------------------001insert 触发器 end------------------------------------ ----------------------------------002update 触发器 start---------------------------------- --update 触发器 (deleted\inserted) --deleted表存放修改前的老数据 + inserted表存放修改后的新数据 create trigger trigger_student_Update on student for Update -- 也可以写成 after Update as --TSQL list , 打印修改前、修改后的数据 declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) --deleted表存放修改前的老数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from deleted print '修改前数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --inserted表存放修改后的新数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print '修改后数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --其他操作 insert into student (student_name,student_age,student_sex)values('kunkun',18,'男') go --update 触发器,触发测试 update student set student_name='王麻子' where student_name='王五' select * from student ----------------------------------002update 触发器 end------------------------------------ go --创建学生表数据备份表 if(OBJECT_ID('studentbak','U')is null) --is not null create table studentbak ( student_id int identity(1,1) primary key, student_name varchar(10), student_age int, student_sex varchar(2) ); ----------------------------------003delete 触发器 start---------------------------------- create trigger trigger_student_Delete on student for delete -- 也可以写成 after delete as --TSQL list , 打印修改前、修改后的数据 declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) --deleted表存放修改前的老数据 select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from deleted print '删除的数据为:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex --其他操作 insert into student (student_name,student_age,student_sex)values('yangyang',18,'女') --可备份至其他备份表中(目标表存在) insert into studentbak(student_name,student_age,student_sex)values(@student_name,@student_age,@student_sex) --目标表不存在 --select * from into studentbak from deleted 存在标识列,不可使用 * --select student_name,student_age,student_sex from deleted go --delete 触发器,触发测试 delete from student where student_name='王麻子' go select * from student select * from studentbak ----------------------------------003delete 触发器 end------------------------------------ ----------------------------------004 instead of insert触发器 start------------------------------------ create trigger trigger_student_InsteadOfInsert on student instead of insert as declare @student_id int,@student_name varchar(50),@student_age varchar(50),@student_sex varchar(50) select @student_id=student_id,@student_name=student_name,@student_age=student_age,@student_sex=student_sex from inserted print '要添加的数据:'+ convert(varchar,@student_id)+','+@student_name+','+@student_age+','+@student_sex go --instead of insert 触发触发器,发生在插入操作之前 insert into student (student_name,student_age,student_sex)values('坤坤2',18,'男') select * from student --after insert:在触发器中,标识列已经自动生成,有值 --instead of insert:在触发器中,标识列没有生成,没有值,执行完成,插入的数据不能正常插入 ----------------------------------004 instead of insert触发器 end--------------------------------------