zoukankan      html  css  js  c++  java
  • 杂谈--DML触发器学习

    触发器按类型分为三类:

    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