请使用支持现代 CSS 与 JavaScript 的浏览器播放课件
DBPA · 5.2

触发器

让数据库服务器按规则自动响应数据变化

VER. 2608.2 Built with impress.js

学习目标

触发器用事件—条件—动作规则自动响应数据变化,并补足声明式约束难以表达的动作

  1. 01用事件—条件—动作拆解一个触发器规则
  2. 02区分 BEFORE 与 AFTER,并说明事务提交边界
  3. 03用“影响 100 行”的 UPDATE 比较 ROW 与 STATEMENT
  4. 04按 INSERT、UPDATE、DELETE 判断 OLD/NEW 是否可用
  5. 05解释动作失败、同类排序和触发链的影响
  6. 06在声明式约束、触发器和应用流程之间选择
2/14
LEARNING OBJECTIVES

触发器是事件—条件—动作规则

事件发生时先判断条件,再决定是否执行动作

触发器把事件、条件和动作连接成自动执行流程
Takeaway

事件 → 条件 → 动作

事件

事件可以是 INSERT、DELETE 或 UPDATE

条件

标准示意可用 WHEN;MySQL 8.4 在动作体中使用 IF 或条件表达式

动作

动作可以修正 NEW、拒绝操作、写入日志或联动其他对象

3/14
ECA RULE

定义触发器要明确七个要素

语法结构服务于事件、时机、粒度和动作语义

要素需要说明例子
1 名称如何识别和维护SC_T
2 目标表哪张基本表被监听ON SC
3 事件哪类变化激活INSERTDELETEUPDATE
4 时机变化前还是后BEFOREAFTER
5 粒度一行还是一条语句FOR EACH ROWFOR EACH STATEMENT
6 引用与条件读哪些值,何时执行OLDNEWWHENIF
7 动作体记录、修正或联动什么日志插入或过程块
Takeaway

触发器只能直接定义在基本表上;标准与其他 DBMS 的语法不能直接当作 MySQL 8.4 脚本

4/14
TRIGGER DEFINITION

BEFORE/AFTER:每个触发器只有一个时机

同一事件可以分别定义 BEFORE 与 AFTER 触发器,因此一次操作可能经过两个阶段

BEFORE

BEFORE 在 INSERT/UPDATE 写入前检查或调整 NEW,也可以拒绝操作;例如把工资下限修正为 4000

AFTER

AFTER 在目标行操作成功后记录 OLD/NEW 或联动其他对象,但不等于事务已经提交

引用

INSERT 通常只有 NEW,UPDATE 有 OLD/NEW,DELETE 通常只有 OLD

Takeaway

AFTER 表示目标行操作成功后执行;后续语句失败时,事务型表中的相关变化仍可能回滚

5/14
BEFORE AND AFTER

行级与语句级决定触发次数

一次批量更新不等于一次行级动作

SQL
UPDATE SC
SET Grade = Grade + 1
WHERE Cno = 'C01';
粒度触发次数适合场景
行级每影响一行一次记录 OLD 与 NEW
语句级(标准/其他 DBMS)每条 SQL 一次统计整批变化
选择依据单行事实或集合事实与动作体需求一致
Takeaway

语句级触发器可使用 OLD TABLE/NEW TABLE 等变化集合,具体语法依 DBMS;MySQL 8.4 只支持 FOR EACH ROW

6/14
ROW AND STATEMENT

OLD 与 NEW 把变化前后连接起来

行级更新可以比较旧值和新值

SQL
-- MySQL 8.4 结构示意,不是完整实验脚本
-- 前置:SC、SC_U(Sno, Cno, OldGrade, NewGrade) 已预先创建
-- BEGIN...END 执行时需由客户端设置 DELIMITER
DELIMITER //
CREATE TRIGGER SC_T
AFTER UPDATE ON SC
FOR EACH ROW
BEGIN
  IF OLD.Grade IS NOT NULL
     AND NEW.Grade IS NOT NULL
     AND NEW.Grade >= 1.1 * OLD.Grade THEN
    INSERT INTO SC_U(Sno, Cno, OldGrade, NewGrade)
    VALUES (OLD.Sno, OLD.Cno, OLD.Grade, NEW.Grade);
  END IF;
END//
DELIMITER ;
Takeaway

MySQL 8.4 用 OLDNEW 与动作体中的 IF;INSERT、UPDATE、DELETE 可用引用不同,标准 SQL 的 REFERENCINGWHEN 不能直接复制

7/14
OLD AND NEW

触发器执行顺序会影响最终状态

同一张表上的多个规则需要可观察、可测试

原始事件

INSERT、UPDATE 或 DELETE

触发动作

可能修改目标表之外的对象

后续事件

动作可能再次激活其他触发器;该流程按每个受影响行表示简化顺序

Takeaway

MySQL 8.4 同一事件、同一时机的多个触发器默认按创建顺序执行;FOLLOWSPRECEDES 用于创建时声明相对顺序

8/14
EXECUTION ORDER

触发器失败或连锁激活会放大影响

触发器的自动动作也会带来隐式副作用

动作体失败

当前语句可能失败;事务型表在事务边界内通常一起回滚,需检查存储引擎和事务边界

多个触发器

同类规则的顺序会影响结果,需确认创建顺序或产品排序规则

触发链

一个动作可能激活另一个规则,需检查循环与级联副作用

Takeaway

是否整体回滚取决于事务、存储引擎和外部副作用;外码级联动作不会激活 MySQL 触发器,触发器也不能修改正在被激活语句使用的同一张表

9/14
FAILURE AND CHAIN

触发器也是需要维护的数据库对象

删除触发器前要确认依赖它的业务和测试

SQL
-- MySQL 8.4:实际产品语法
DROP TRIGGER IF EXISTS SC_T;

-- 标准 SQL 示意,不要直接当作 MySQL 脚本
DROP TRIGGER SC_T ON SC;
Takeaway

MySQL 8.4 使用无 ON 的形式并需要关联表的 TRIGGER 权限;删除不会自动删除日志表,具体语法随 DBMS 变化

10/14
DROP TRIGGER

声明式约束优先表达直接完整性规则

触发器补足需要动作、历史或跨表联动的规则,不应隐藏所有业务规则

需求优先机制触发器价值
非空、范围、唯一NOT NULLCHECKUNIQUE不必增加隐式过程
主码、外码PRIMARY KEYFOREIGN KEYDBMS 原生检查
记录变化历史约束无法完成AFTER 动作写日志
写入前自动修正约束难以表达BEFORE 动作调整值
批量统计变化显式 SQL 或应用流程MySQL 8.4 没有语句级触发器
复杂跨表联动触发器或应用流程评估事务边界、触发链、性能和可观察性
Takeaway

能用声明式约束表达的规则优先使用约束;触发器主要补足历史记录、自动修正和复杂联动

11/14
CHOOSE THE MECHANISM

用 ECA、时机和风险判断触发器

触发器让数据库对事件自动反应,但使用时必须控制隐式复杂度

ECA

触发器由事件、条件和动作组成

时机

BEFORE/AFTER 决定执行时机,AFTER 不等于事务提交

粒度与引用

ROW/STATEMENT 决定触发次数,INSERT、UPDATE、DELETE 对 OLD/NEW 的可用引用不同

风险与选择

失败、触发链、性能和产品边界决定是否应优先使用声明式约束

Takeaway

先判断声明式约束是否足够,再引入触发器;MySQL 8.4 只支持行级触发器

12/14
RECAP

本节知识地图

13/14
KNOWLEDGE MAP

本节问题

  1. 01SC.Grade 从 60 变 70;若提高至少 10% 就写 SC_U,事件、条件、动作和时机各是什么?
  2. 02UPDATE 影响 100 行时,ROW/STATEMENT 各触发几次?MySQL 8.4 支持哪种?三类 DML 中 OLD/NEW 各何时可用?
  3. 03工资下限修正与成绩日志各选 BEFORE 还是 AFTER?AFTER 是否等于 COMMIT?
  4. 04SC_T 写 SC_U,SC_U 的触发器失败时,原 UPDATE 可能怎样?
  5. 05哪些规则应优先用 CHECK、PRIMARY KEY、FOREIGN KEY?标准 SQL 与 MySQL 8.4 语法有何差异?
14/14
CHECK YOUR UNDERSTANDING