触发器按类型分为三类:

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. Office办公软件(Excel PPT Word)使用整理

    Office办公软件(Excel PPT Word)使用整理.. -------------- Excel默认打印预览于当前连接的打印机的纸张大小保持一致. Excel sheet不见了怎么办 --- ...

  2. 装饰模式(decorator)

    意图: 动态地给一个对象添加一些额外的职责,就增加功能而言,Decorator模式相比生成子类模式更为灵活 动机: 有时我们希望给某个对象而不是整个类添加一些功能.例如,一个图形用户界面工具箱允许你对 ...

  3. web前端笔记整理一---HTML

    一 HTML标签1 页面及标记 1 HTML 文件结构 拓展名 .html或者.htm <!DOCTYPE html>// 声明 <html> html 主体 <head ...

  4. Android学习之旅(一)

    2017-02-27 今天开始,正式开启Android学习之旅,背景从事.Net平台开发快五年了,一直在用C#做Web开发. 前天选购了两本书:<Java 编程思想(第四版)>和<第 ...

  5. (转)新手写爬虫v2.5(使用代理的异步爬虫)

    开始 开篇:爬代理ip v2.0(未完待续),实现了获取代理ips,并把这些代理持久化(存在本地).同时使用的是tornado的HTTPClient的库爬取内容. 中篇:开篇主要是获取代理ip:中篇打 ...

  6. Linux企业运维人员最常用150个命令汇总

    命令 功能说明 线上查询及帮助命令(2个) man 查看命令帮助,命令的词典,更复杂的还有info,但不常用. help 查看Linux内置命令的帮助,比如cd命令. 文件和目录操作命令(18个) l ...

  7. python的multiprocessing模块进程创建、资源回收-Process,Pool

    python的multiprocessing有两种创建进程的方式,每种创建方式和进程资源的回收都不太相同,下面分别针对Process,Pool及系统自带的fork三种进程分析. 1.方式一:fork( ...

  8. Java对象中的finalize()方法使用说明

    我在写java程序的时候,从来没有遇见过是用Object.finalize()的时候,但是在学习Java虚拟机的垃圾回收机制时,虚拟机在进行垃圾回收时需要使用finalize()方法来进行对象清理.下 ...

  9. LNMP1.4 PHP升级脚本

    升级PHP前,请确认你的网站程序是否支持升级到的PHP版本,防止升级到网站程序不兼容的PHP版本,具体可以去你使用的PHP程序的官网查询相关版本支持信息.v1.3及以后版本大部分情况下也可以进行降级操 ...

  10. SVN 通过IIS设置反向代理访问

    原因 一个字,穷,没办法,只有一台机器 要当测试服务器还要做源码管理. 解决办法 通过IIS配置反向代理访问SVN,给SVN访问的HTTPS绑定上域名,就可以正常访问了. 1.修改SVN配置 把SVN ...