触发器的定义
触发器(trigger)是sql server提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发,比如当对一个表进行操作( insert,delete, update)时就会激活它执行。
触发器的作用
触发器的主要作用就是其能够实现由主键和外键所不能保证的复杂参照完整性和数据的一致性,它能够对数据库中的相关表进行级联修改,提高比check约束更复杂的的数据完整性,并自定义错误消息。
触发器的主要作用主要有以下接个方面
- 强制数据库间的引用完整性
- 级联修改数据库中所有相关的表,自动触发其它与之相关的操作
- 跟踪变化,撤销或回滚违法操作,防止非法修改数据
- 返回自定义的错误消息,约束无法返回信息,而触发器可以
- 触发器可以调用更多的存储过程
触发器的优点
- 触发器是自动的。当对表中的数据做了任何修改之后立即被激活。
- 触发器可以通过数据库中的相关表进行层叠修改。
- 触发器可以强制限制。这些限制比用check约束所定义的更复杂。与check约束不同的是,触发器可以引用其他表中的列。
触发器的分类
sql server包括三种常规类型的触发器:dml触发器、ddl触发器和登录触发器。
dml(数据操作语言,data manipulation language)触发器
dml触发器是一些附加在特定表或视图上的操作代码,当数据库服务器中发生数据操作语言事件时执行这些操作。
sql server中的dml触发器有三种:
- insert触发器:向表中插入数据时被触发;
- delete触发器:从表中删除数据时被触发;
- update触发器:修改表中数据时被触发。
当遇到下列情形时,应考虑使用dml触发器:
- 通过数据库中的相关表实现级联更改
- 防止恶意或者错误的insert、delete和update操作,并强制执行check约束定义的限制更为复杂的其他限制。
- 评估数据修改前后表的状态,并根据该差异才去措施。
ddl(数据定义语言,data definition language)触发器
ddl触发器是当服务器或者数据库中发生数据定义语言(主要是create,drop,alter开头的语句)事件时被激活使用,使用ddl触发器可以防止对数据架构进行的某些更改或记录数据中的更改或事件操作。
登录触发器
登录触发器将为响应 login 事件而激发存储过程。与 sql server 实例建立用户会话时将引发此事件。登录触发器将在登录的身份验证阶段完成之后且用户会话实际建立之前激发。因此,来自触发器内部且通常将到达用户的所有消息(例如错误消息和来自 print 语句的消息)会传送到 sql server 错误日志。如果身份验证失败,将不激发登录触发器。
触发器的工作原理
触发器触发时:
- 系统自动在内存中创建inserted表或deleted表;
- 只读,不允许修改,触发器执行完成后,自动删除。
inserted表:
- 临时保存了插入或更新后的记录行;
- 可以从inserted表中检查插入的数据是否满足业务需求;
- 如果不满足,则向用户发送报告错误消息,并回滚插入操作。
deleted表:
- 临时保存了删除或更新前的记录行;
- 可以从deleted表中检查被删除的数据是否满足业务需求;
- 如果不满足,则向用户报告错误消息,并回滚插入操作。
inserted表和deleted表对照:
创建触发器
创建触发器的语法:
create trigger trigger_name on table_name [with encryption] for | after | instead of [delete, insert, update] as t-sql语句go
注:
with encryption 表示加密触发器定义的sql文本
delete, insert, update指定触发器的类型
触发器示例
创建学生表
create table student( stu_id int identity(1,1) primary key, stu_name varchar(10), stu_gender char(2), stu_age int )
创建insert触发器
--创建insert触发器 create trigger trig_insert on student after insert as begin --判断student_sum表是否存在 if object_id(n'student_sum',n'u') is null --创建存储学生人数的student_sum表 create table student_sum( stucount int default(0) ); declare @stunumber int; select @stunumber = count(*)from student; --判断表中是否有记录 if not exists (select * from student_sum) insert into student_sum values(0); update student_sum set stucount =@stunumber; --把更新后总的学生数插入到student_sum表中 end --测试触发器trig_insert --功能是向student插入数据的同时级联插入到student_sum表中,更新stucount --因为是后触发器,所以先插入数据后,才触发触发器trig_insert; insert into student(stu_name,stu_gender,stu_age) values('吕布','男',30); select stucount 学生总人数 from student_sum; insert into student(stu_name,stu_gender,stu_age) values('貂蝉','女',30); select stucount 学生总人数 from student_sum; insert into student(stu_name,stu_gender,stu_age) values('曹阿瞒','男',40); select stucount 学生总人数 from student_sum;
执行上面的语句后,结果如下图所示:
既然定义了学生总数表student_sum表是向student表中插入数据后才计算学生总数的,所以学生总数表应该禁止用户向其中插入数据
--创建insert_forbidden,禁止用户向student_sum表中插入数据 create trigger insert_forbidden on student_sum after insert as begin raiserror('禁止直接向该表中插入记录,操作被禁止',1,1) --raiserror 是用于抛出一个错误 rollback transaction end --触发触发器insert_forbidden insert student_sum (stucount) values(5);
结果如下:
创建delete触发器
用户执行delete操作,就会激活delete触发器,从而控制用户能够从数据库中删除数据记录,触发delete触发器后,用户删除的记录会被添加到deleted表中,原来表的相应记录被删除,所以在deleted表中查看删除的记录。
--创建delete触发器 create trigger trig_delete on student after delete as begin select stu_id as 已删除的学生编号, stu_name stu_gender, stu_age from deleted end; --执行一条delete语句触发trig_delete触发器 delete from student where stu_id=1;
结果如下:
创建update触发器
update触发器是当用户在指定表上执行update语句时被调用被调用,这种类型的触发器用来约束用户对数据的修改。update触发器可以执行两种操作:更新前的记录存储在deleted表中,更新后的记录存储在inserted表中。
--创建update触发器 create trigger trig_update on student after update as begin declare @stucount int; select @stucount=count(*) from student; update student_sum set stucount =@stucount; select stu_id as 更新前学生编号, stu_name as 更新前学生姓名 from deleted; select stu_id as 更新后学生编号, stu_name as 更新后学生姓名 from inserted; end --创建完成,执行一条update语句触发trig_update触发器 update student set stu_name='张飞' where stu_id=2;
结果如下:
创建替代触发器
与前面介绍的三种after触发器不同,sql server服务器在执行after触发器的sql代码后,先建立临时的inserted表和deleted表,然后执行代码中对数据库操作,最后才激活触发器中的代码。而对于替代(instead of)触发器,sql server服务器在执行触发instead of触发器的代码时,先建立临时的inserted表和deleted表,然后直接触发instead of触发器,而拒绝执行用户输入的dml操作语句。
--创建instead of触发器 create trigger trig_insteadof on student instead of insert as begin declare @stuage int; select @stuage=(select stu_age from inserted) if(@stuage >120) select '插入年龄错误' as '失败原因' end
创建完成,执行一条insert语句触发触发器trig_insteadof
批注
触发器在早期的数据处理过程中经常使用到,特别是在处理一些因某些动作而需要对其他表进行调整的逻辑时。但是随着数据量的增长,触发器对数据库的性能影响越来越大,容易造成数据库性能降低。所以触发器在数据量大的场景是禁止使用的,但是其逻辑处理功能还是被一直保留,说明其还是有较深的应用场景,需要我们掌握它的相关用法。