触发器按类型分为三类:

1. DML 触发器,在数据变更时触发;

2. DDL 触发器,在修改数据库级别或实例级别对象时触发;

3. Login 触发器,在用户登录时触发;

最常见的是DML触发器,DML触发器又可以分为两类: INSTEAD OF触发器和AFTER触发器(部分书上有提到FOR触发器,其实就是AFTER 触发器,只是写法不同而已)。

从功能来看,INSTEAD OF触发器用来替换实际的数据修改操作,而AFTER触发器用来在实际操作完成后进行后续操作。例如对于DELETE操作,如果我们期望只修改数据状态来标示数据已被删除而不是将数据从表中删除,那么我们可以使用INSTEAD OF触发器来实现;如果我们期望在删除数据后在其他表记录删除操作的发生时间,那么我们可以使用AFTER触发器来实现。

从执行来看,INSTEAD OF触发器和AFTER触发器的所处的执行时期不同,SQL Server中的触发顺序为:

1. 触发INSTEAD OF触发器

2. 触发DEFAULT 约束

3. 触发主键/唯一/CHECK约束

4. 触发外键约束

5. 触发AFTER 触发器

因此如果期望修改操作顺利执行而不触发约束导致回滚的话,可以使用INSTEAD OF触发器来将实现(在INSTEAD OF 触发器中修改使数据满足约束条件)。

因为INSTEAD OF 触发器改写了实际要发生的修改操作,因此每个表上每种修改类型(DELETE/INSERT/UPDATE)只能有一个INSTEAD OF 触发器;而AFTER 触发器没有类似限制,可以创建多个AFTER触发器。

问题来了,在存在多个AFTER触发器情况下,AFTER触发器按什么顺序来执行呢?SQL Server允许针对每种修改类型(DELETE/INSERT/UPDATE)指定一个最先触发和最后触发的AFTER触发器,但不能控制其余的触发器触发顺序。

指定最先执行的AFTER触发器:

--指定针对INSERT操作最先触发的AFTER触发器
EXEC sys.sp_settriggerorder
@triggername='tr_TB1_INSERT',
@order='First',
@stmttype='INSERT'

说完触发顺序,再来说道说道触发次数,装逼的说法为:DML trrigers have statement scope and only fire just once regardless of how many rows affected.通俗说法就是对于一条语句,不管语句修改了多少行(0行或者1000行),对应该操作类型的触发器都会被触发并且只触发一次。

PS:上面说的Fire only once只是针对执行的SQL语句,并不包含该触发器内部的SQL语句

SQL server中有两种特殊的触发器:嵌套(Nested)触发器和递归(Recursive)触发器,由Demo来解释下:

嵌套(Nested)触发器:在TB1和TB2上创建触发器,当TB1上TR_TB1_INSERT1被触发时,TR_TB1_INSERT1中的语句执行导致TB2上TR_TB2_INSERT1被触发

--================================
--在TB1和TB2上创建触发器,当TB1上TR_TB1_INSERT1被
--触发时,TR_TB1_INSERT1中的语句执行导致TB2上
--TR_TB2_INSERT1被触发,即属于Nested触发器
CREATE TRIGGER TR_TB1_INSERT1
ON dbo.TB1
AFTER INSERT
AS
BEGIN
INSERT INTO TB2(C1)
SELECT C1 FROM inserted END
GO CREATE TRIGGER TR_TB2_INSERT1
ON dbo.TB2
AFTER INSERT
AS
BEGIN SELECT 1 END

递归(Recursive)触发器可分为直接递归(Directed Recursive)触发器和间接递归(Indirect Recursive)触发器

直接递归(Directed Recursive)触发器:

在TB1创建触发器,当TB1上TR_TB1_INSERT1被触发时,TR_TB1_INSERT1中的语句执行导致TB1上TR_TB1_INSERT1再次被触发

--================================
--在TB1创建触发器,当TB1上TR_TB1_INSERT1被触发时,
--TR_TB1_INSERT1中的语句执行导致TB1上TR_TB1_INSERT1
--再次被触发,即属于直接递归(Directed Recursive)触发器。
ALTER TRIGGER TR_TB1_INSERT1
ON dbo.TB1
AFTER INSERT
AS
BEGIN
--限制递归层数为10层
IF(@@NESTLEVEL<10)
BEGIN
INSERT INTO TB1(C1)
SELECT C1+1 FROM inserted
END
END
GO

间接递归(Indirect Recursive)触发器:

在TB1和TB2上创建触发器,当TB1上TR_TB1_INSERT1被触发时,TR_TB1_INSERT1中的语句执行导致TB2上TR_TB2_INSERT1被触发,而TB2上TR_TB2_INSERT1的触发器执行时又导致TB1上TR_TB1_INSERT1被触发,从而引发循环。

--================================
--在TB1和TB2上创建触发器,当TB1上TR_TB1_INSERT1被
--触发时,TR_TB1_INSERT1中的语句执行导致TB2上
--TR_TB2_INSERT1被触发,而TB2上TR_TB2_INSERT1的
--触发器执行时又导致TB1上TR_TB1_INSERT1被触发,从而
--引发循环,即间接递归(Indirect Recursive)触发器
CREATE TRIGGER TR_TB1_INSERT1
ON dbo.TB1
AFTER INSERT
AS
BEGIN
IF(@@NESTLEVEL<10)
BEGIN
INSERT INTO TB2(C1)
SELECT C1 FROM inserted
END END
GO CREATE TRIGGER TR_TB2_INSERT1
ON dbo.TB2
AFTER INSERT
AS
BEGIN
IF(@@NESTLEVEL<10)
BEGIN
INSERT INTO TB1(C1)
SELECT C1 FROM inserted
END
END

需要注意的是:

1. 嵌套(Nested)触发器在sys.configurations中配置,默认开启

2. (Recursive)触发器在数据库级别配置,默认为关闭,即不允许直接递归(Directed Recursive)触发器,但不影响间接递归(Indirect Recursive)触发器,如果需要禁用递归(Indirect Recursive)触发器,需要同时禁用嵌套(Nested)触发器和(Recursive)触发器

3. 由于嵌套触发器会消耗大量资源(需要保留每层触发器的上下文以便回滚),因此默认限制最多嵌套32层。

行版本(Row version)

在SQL Server多中功能中使用到row version来保留多个版本的数据,这些功能有:

1. MARS

2. Triggers

3. Online indexing

4. Optimistic Transaction Isolation Levels

因此在使用触发器时,应考虑到可能会为表增加额外14bytes的行版本存储指针

如下面例子中,表中数据被删除一半,但由于数据只是表示为gost,尚未真正移除,而由于触发器存在,每行额外增加14byte的数据,从而导致页拆分,最终使得删除操作完成后表反而增大。

测试代码:

USE tempdb
--================================
--创建测试表
DROP TABLE TB1
GO
CREATE TABLE TB1
(
ID INT IDENTITY(1,1) PRIMARY KEY,
C2 INT NOT NULL,
C3 VARCHAR(MAX)
)
GO
--================================
--创建Delete触发器
CREATE TRIGGER TR_TB1_DELETE
ON dbo.TB1
AFTER DELETE
AS
BEGIN
RETURN
END
GO
--================================
--插入5w数据
INSERT INTO TB1(C2)
SELECT TOP(5000) 1 AS C2 FROM sys.all_columns T
GO 10 --================================
--查看表TB1使用的页
DBCC TRACEON(3604)
GO
DBCC IND('tempdb','TB1',1)
GO
--================================
--删除一半的数据
DELETE FROM dbo.TB1
WHERE ID%2=0 GO
--================================
--查看表TB1使用的页
DBCC TRACEON(3604)
GO
DBCC IND('tempdb','TB1',1)
GO

PS: 如果表中不存在LOB或者VARCHAR(MAX)之类的大字段,不存在ROW_OVERFLOW数据页,则SQL Server不会为每行增加14byte的行版本存储指针

--==============================================================

--额外补充

1. 如果使用Merge并且设置了INSERT/DELETE/UPDATE方法,那么即使没有满足条件的数据进行INSERT/DELETE/UPDATE,也会触发INSERT/DELETE/UPDATE相关的触发器。

--==================================================

新一年,换换口味,来点萌妹子吧!

杂谈--DML触发器学习的更多相关文章

  1. 触发器三(行级DML触发器)(学习笔记)

    行级DML触发器 每当一条记录出现更新操作时进行触发操作定义时要定义FOR EACH ROW 使用":old.字段"和":new.字段"标识符 No. 触发语句 ...

  2. 触发器二(DML触发器)(学习笔记)

    DML触发器(语句触发器) 由DML语句进行触发,当用户执行了INSERT,UPDATE,DELETE操作时就会触发操作 示例一.只有在每个月的10日才允许办理,新员工入职与离职,其他时间不允许增加和 ...

  3. SQL触发器学习

      简介 触发器是一种特殊类型的存储过程.触发器分为: DML( 数据操纵语言 Data Manipulation Language)触发器:数据库中表或视图的数据更改时触发,包括insert,upd ...

  4. SQL Server 数据库DML触发器 【一】

    今天学习SQL Server数据库中DML触发器(DDL触发器以后有时间继续学习). 当删除一条创建有触发器的表中的内容时,触发器执行SQL语句. 1.首相先创建一张表,表名称是 [Test] , 内 ...

  5. 触发器学习笔记(:new,:old用法)

    触发器学习笔记(:new,:old用法) 触发器是数据库发生某个操作时自动运行的一类的程序         用于保持数据的完整性或记录数据库操作信息方面         触发器不能够被直接调用,只能够 ...

  6. oracle触发器学习

    转自:http://blog.csdn.net/indexman/article/details/8023740/ 本篇主要内容如下: 8.1 触发器类型 8.1.1 DML触发器 8.1.2 替代触 ...

  7. DML触发器1

    数据库触发器是特殊的存储程序.通常不直接调用它们,而是由数据库的事件触发. 触发器分类: >DML触发器 >instead-of触发器 >系统事件触发器,还可以是DDL 一. DML ...

  8. SQL语句(二十一)—— 触发器(DML触发器)

    触发器 一 .触发器概述(特殊的存储过程) 定义: 在修改指定表值的数据时执行的 存储过程. 不同的是 : 执行存储过程要使用EXEC语句来调用,而触发器的执行不需要使用EXEC语句来调用. 作用: ...

  9. SQL_触发器学习

    --触发器学习-------------------------------------------------------------------------------after 触发器----- ...

随机推荐

  1. HTML5无插件多媒体Media——音频audio与视频video

    文件日志地址  http://blog.csdn.net/q1056843325/article/details/60336226 音频与视频现在已经变得越来越流行 各个网站为了保证跨浏览器的兼容性 ...

  2. 使用Coding.net+Hexo+node.js+git来搭建个人博客

    使用Coding.net来搭建基于Hexo的博客 一.准备工作 什么是Coding.net Coding可以说,就是国产的Github,但是,有一个功能使它似乎超越了GitHub-那就是 Web ID ...

  3. Java基础---网络编程

    第一讲     概述 1.网络模型:OSI参考模型和TCP/IP参考模型 图示: 一般来说开发处于传输层和网际层,应用层为:FTP和HTTP协议等,传输层为:UDP和TCP等,网际层为:IP. 通常用 ...

  4. Spring4整合quartz2.2.3,quartz动态任务

    Spring4整合quartz2.2.3,quartz动态任务 >>>>>>>>>>>>>>>>> ...

  5. 利用Google浏览器调试js代码

    1.js有两种引入方式,外链和内嵌: 内嵌在浏览中直接调试,外链要在断点处写debugger; 示例代码: <!DOCTYPE html> <html lang="en&q ...

  6. SVG渐变

    前面的话 给SVG元素应用填充和描边,除了使用纯色外,还可以使用渐变.本文将详细介绍SVG渐变 线性渐变 有两种类型的渐变:线性渐变和径向渐变.必须给渐变内容指定一个id属性,否则文档内的其他元素不能 ...

  7. c# 网页打印全流程

    说明:我要实现的就是将数据库中Group表的数据查找出来,替换打印模版中的内容,再将模版文件打印出来 1.准备好要打印的模版group_O_train.html <div class=" ...

  8. 【集美大学1411_助教博客】团队作业6——展示博客(Alpha版本)

    写在前面的话 工作还真是应该抓紧做呢,以下评分是助教在出差前评的,但出差回来就忘记了大部分内容.同学们都在预期时间内完成了自己的alpha项目.由于助教的频繁出差,评分工作落下一大截,在此表示欠意,会 ...

  9. 201521123107 《Java程序设计》第6周学习总结

    第6周作业-接口.内部类与Swing 1.本周学习总结 2.书面作业 1.clone方法 1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注 ...

  10. 团队作业4——第一次项目冲刺(Alpha版本)2017.4.24

    在下午3-4节Linux课结束后,我们teamworkers全体队员留在禹州楼304进行约20分钟的短暂会议,会议讨论关于昨天任务的总结并分配了今天的新任务,大家畅所欲言,情绪高昂,各自阐述了自己不一 ...