触发器按类型分为三类:

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. DOM 对象方法

    DOM 对象方法 这里提供一些您将在本教程中学到的常用方法: 方法 描述 getElementById() 返回带有指定 ID 的元素. getElementsByTagName() 返回包含带有指定 ...

  2. (转)没有IE就没有伤害!浏览器兼容性问题解决方案汇总

    普及:浏览器的兼容性问题,往往是个别浏览器(没错,就是那个与众不同的浏览器)对于一些标准的定义不一致导致的.俗话说:没有IE就没有伤害. 贴士:内容都是自己总结的,不免会出现错误或者bug,欢迎更正和 ...

  3. 记一次帮客户做SEO诊断:新站收录后优化一个月了,排名一直没上去

    一.网站问题 新网站建设有一个多月了,一直在优化,但是网站一直没有排名,不知道什么原因.接下来针对这个网站分析一下原因. 二.诊断分析 看一下网站的基础数据,如图: < 可以看出来网站只有一个多 ...

  4. 数据库-MYSQL安装配置和删除

    * 课程回顾: * 完成注册和登陆的功能. * 准备的工作 * 技术.开源jar包 * 开发的功能使用MVC模式 * C:控制层(接收请求和从客户端发送过来的参数) * 接收参数(request对象) ...

  5. c++非STL全排列

    #include <cstdio> #include <vector> #include <string> #include <iostream> #i ...

  6. 服务端性能测试 TPS

     针对服务器端的性能,以TPS为主来衡量系统的性能,并发用户数为辅来衡量系统的性能,如果必须要用并发用户数来衡量的话,需要一个前提,那就是交易在多长时间内完成,因为在系统负载不高的情况下,将思考时间( ...

  7. 【全面总结】js获取元素位置大小

    [js获取元素位置+元素大小]全面总结 目录 1.关于offset offsetParent(只读) offsetTop(只读) offsetLeft(只读) offsetHeight(只读) off ...

  8. GIF、JPEG 和 PNG的区别在哪…

    原文地址:GIF.JPEG 和 PNG的区别在哪里?作者:苗得雨 GIF.JPEG 和 PNG 是三种最常见的图片格式. GIF:1987 年诞生,常用于网页动画,使用无损压缩,支持 256 种颜色( ...

  9. 超级简单实用的前端必备技能-javascript-全屏滚动插件

      fullPage.js fullPage.js是一个基于jQuery的全屏滚动插件,它能够很方便.很轻松的制作出全屏网站 本章内容将详细介绍Android事件的具体处理及常见事件. 主要功能 支持 ...

  10. 【Alpha阶段】第四次 Scrum Meeting

    每日任务 1.本次会议为第 四次 Meeting会议: 2.本次会议在上午09:35,大课间休息时间在禹州楼召开,召开本次会议为20分钟,分析完善接下来的任务: 一.今日站立式会议照片 二.每个人的工 ...