事件
事件可以是 INSERT、DELETE 或 UPDATE
让数据库服务器按规则自动响应数据变化
触发器用事件—条件—动作规则自动响应数据变化,并补足声明式约束难以表达的动作
事件发生时先判断条件,再决定是否执行动作
事件 → 条件 → 动作
事件可以是 INSERT、DELETE 或 UPDATE
标准示意可用 WHEN;MySQL 8.4 在动作体中使用 IF 或条件表达式
动作可以修正 NEW、拒绝操作、写入日志或联动其他对象
语法结构服务于事件、时机、粒度和动作语义
| 要素 | 需要说明 | 例子 |
|---|---|---|
| 1 名称 | 如何识别和维护 | SC_T |
| 2 目标表 | 哪张基本表被监听 | ON SC |
| 3 事件 | 哪类变化激活 | INSERT、DELETE、UPDATE |
| 4 时机 | 变化前还是后 | BEFORE、AFTER |
| 5 粒度 | 一行还是一条语句 | FOR EACH ROW、FOR EACH STATEMENT |
| 6 引用与条件 | 读哪些值,何时执行 | OLD、NEW、WHEN、IF |
| 7 动作体 | 记录、修正或联动什么 | 日志插入或过程块 |
触发器只能直接定义在基本表上;标准与其他 DBMS 的语法不能直接当作 MySQL 8.4 脚本
同一事件可以分别定义 BEFORE 与 AFTER 触发器,因此一次操作可能经过两个阶段
BEFORE 在 INSERT/UPDATE 写入前检查或调整 NEW,也可以拒绝操作;例如把工资下限修正为 4000
AFTER 在目标行操作成功后记录 OLD/NEW 或联动其他对象,但不等于事务已经提交
INSERT 通常只有 NEW,UPDATE 有 OLD/NEW,DELETE 通常只有 OLD
AFTER 表示目标行操作成功后执行;后续语句失败时,事务型表中的相关变化仍可能回滚
一次批量更新不等于一次行级动作
01
02
03UPDATE SC
SET Grade = Grade + 1
WHERE Cno = 'C01';| 粒度 | 触发次数 | 适合场景 |
|---|---|---|
| 行级 | 每影响一行一次 | 记录 OLD 与 NEW |
| 语句级(标准/其他 DBMS) | 每条 SQL 一次 | 统计整批变化 |
| 选择依据 | 单行事实或集合事实 | 与动作体需求一致 |
语句级触发器可使用 OLD TABLE/NEW TABLE 等变化集合,具体语法依 DBMS;MySQL 8.4 只支持 FOR EACH ROW
行级更新可以比较旧值和新值
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16-- 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 ;MySQL 8.4 用 OLD/NEW 与动作体中的 IF;INSERT、UPDATE、DELETE 可用引用不同,标准 SQL 的 REFERENCING/WHEN 不能直接复制
同一张表上的多个规则需要可观察、可测试
INSERT、UPDATE 或 DELETE
可能修改目标表之外的对象
动作可能再次激活其他触发器;该流程按每个受影响行表示简化顺序
MySQL 8.4 同一事件、同一时机的多个触发器默认按创建顺序执行;FOLLOWS/PRECEDES 用于创建时声明相对顺序
触发器的自动动作也会带来隐式副作用
当前语句可能失败;事务型表在事务边界内通常一起回滚,需检查存储引擎和事务边界
同类规则的顺序会影响结果,需确认创建顺序或产品排序规则
一个动作可能激活另一个规则,需检查循环与级联副作用
是否整体回滚取决于事务、存储引擎和外部副作用;外码级联动作不会激活 MySQL 触发器,触发器也不能修改正在被激活语句使用的同一张表
删除触发器前要确认依赖它的业务和测试
01
02
03
04
05-- MySQL 8.4:实际产品语法
DROP TRIGGER IF EXISTS SC_T;
-- 标准 SQL 示意,不要直接当作 MySQL 脚本
DROP TRIGGER SC_T ON SC;MySQL 8.4 使用无 ON 的形式并需要关联表的 TRIGGER 权限;删除不会自动删除日志表,具体语法随 DBMS 变化
触发器补足需要动作、历史或跨表联动的规则,不应隐藏所有业务规则
| 需求 | 优先机制 | 触发器价值 |
|---|---|---|
| 非空、范围、唯一 | NOT NULL、CHECK、UNIQUE | 不必增加隐式过程 |
| 主码、外码 | PRIMARY KEY、FOREIGN KEY | DBMS 原生检查 |
| 记录变化历史 | 约束无法完成 | AFTER 动作写日志 |
| 写入前自动修正 | 约束难以表达 | BEFORE 动作调整值 |
| 批量统计变化 | 显式 SQL 或应用流程 | MySQL 8.4 没有语句级触发器 |
| 复杂跨表联动 | 触发器或应用流程 | 评估事务边界、触发链、性能和可观察性 |
能用声明式约束表达的规则优先使用约束;触发器主要补足历史记录、自动修正和复杂联动
触发器让数据库对事件自动反应,但使用时必须控制隐式复杂度
触发器由事件、条件和动作组成
BEFORE/AFTER 决定执行时机,AFTER 不等于事务提交
ROW/STATEMENT 决定触发次数,INSERT、UPDATE、DELETE 对 OLD/NEW 的可用引用不同
失败、触发链、性能和产品边界决定是否应优先使用声明式约束
先判断声明式约束是否足够,再引入触发器;MySQL 8.4 只支持行级触发器
SC.Grade 从 60 变 70;若提高至少 10% 就写 SC_U,事件、条件、动作和时机各是什么?