触发器
触发器(Trigger)是与表关联、在特定事件(INSERT/UPDATE/DELETE)发生时自动执行的一组 SQL 语句。它把"数据变更后的联动处理"固化在数据库层,实现自动审计、自动更新、数据一致性等。
为什么需要触发器
| 场景 | 触发器的作用 |
|---|---|
| 数据审计 | 记录每次增删改的操作日志 |
| 自动更新时间 | 更新 updated_at 字段 |
| 完整性约束 | 复杂业务规则在数据库层强制 |
| 联动更新 | 删订单同时更新用户统计 |
触发器的好处是自动、不依赖应用调用;代价是隐式执行、难以排查(问题可能来自触发器而非业务代码),且影响写入性能。
触发器的类型
按触发时机分:
| 类型 | 触发时机 |
|---|---|
BEFORE | 事件发生前(可修改 NEW 值,常用于校验/补默认值) |
AFTER | 事件发生后(多用于审计、联动更新) |
按触发事件分:INSERT、UPDATE、DELETE。
创建触发器
基本语法(MySQL)
sql
CREATE TRIGGER 触发器名
BEFORE/AFTER INSERT/UPDATE/DELETE ON 表名
FOR EACH ROW -- 每一行受影响都触发
BEGIN
触发逻辑
END示例 1:自动更新时间(BEFORE UPDATE)
sql
DELIMITER //
CREATE TRIGGER trg_heros_before_update
BEFORE UPDATE ON heros
FOR EACH ROW
BEGIN
SET NEW.updated_at = NOW(); -- 修改 NEW 行
END //
DELIMITER ;示例 2:操作审计日志(AFTER INSERT)
sql
-- 审计表
CREATE TABLE heros_log (
id INT AUTO_INCREMENT PRIMARY KEY,
hero_id INT,
action VARCHAR(10),
old_hp INT,
new_hp INT,
log_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
DELIMITER //
CREATE TRIGGER trg_heros_after_update
AFTER UPDATE ON heros
FOR EACH ROW
BEGIN
INSERT INTO heros_log (hero_id, action, old_hp, new_hp)
VALUES (OLD.id, 'UPDATE', OLD.hp_max, NEW.hp_max);
END //
DELIMITER ;NEW 与 OLD 伪行
触发器里用 NEW 和 OLD 访问变更前后的行:
| 伪行 | INSERT | UPDATE | DELETE |
|---|---|---|---|
NEW(新值) | ✅ | ✅ | ❌ |
OLD(旧值) | ❌ | ✅ | ✅ |
sql
-- UPDATE 时,OLD.hp_max 是修改前,NEW.hp_max 是修改后
-- DELETE 时,只有 OLD(被删行)
-- INSERT 时,只有 NEW(新插入行)使用示例:
sql
DELIMITER //
CREATE TRIGGER trg_prevent_delete
BEFORE DELETE ON heros
FOR EACH ROW
BEGIN
-- 禁止删除生命值大于 7000 的英雄
IF OLD.hp_max > 7000 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = '禁止删除高生命英雄';
END IF;
END //
DELIMITER ;
SIGNAL SQLSTATE '45000'主动抛出异常,可用来在触发器中阻止非法操作。
管理触发器
sql
-- 查看触发器
SHOW TRIGGERS;
-- 查看指定表的触发器
SHOW TRIGGERS FROM 数据库名 WHERE `Table` = 'heros';
-- 查看触发器定义
SHOW CREATE TRIGGER trg_heros_after_update;
-- 删除触发器
DROP TRIGGER IF EXISTS trg_heros_after_update;完整实战:自动审计 + 更新统计
需求:删除英雄时记录删除日志,同时更新"各定位英雄总数"统计表。
sql
-- 统计表
CREATE TABLE role_stats (
role_main VARCHAR(20) PRIMARY KEY,
hero_count INT NOT NULL DEFAULT 0
);
-- 删除时:写审计日志 + 更新统计
DELIMITER //
CREATE TRIGGER trg_heros_after_delete
AFTER DELETE ON heros
FOR EACH ROW
BEGIN
-- 1. 写审计日志
INSERT INTO heros_log (hero_id, action, old_hp)
VALUES (OLD.id, 'DELETE', OLD.hp_max);
-- 2. 更新统计表
UPDATE role_stats
SET hero_count = hero_count - 1
WHERE role_main = OLD.role_main;
END //
DELIMITER ;触发器的限制与注意事项
| 注意点 | 说明 |
|---|---|
| 递归触发 | MySQL 默认禁止递归触发器(触发器再触发同一表),需谨慎 |
| 性能开销 | 每条受影响行都执行触发器,大表批量写入会显著变慢 |
| 可排查性 | 触发器隐式执行,问题定位困难,需完善的日志 |
| 事务 | 触发器内的语句与触发它的事务同事务,出错会一起回滚 |
| 触发时机 | BEFORE 可改 NEW(校验/补值),AFTER 只能读 NEW/OLD |
| 不可见性 | 应用层看不到触发逻辑,易产生"意外的数据变化" |
最佳实践:
- 触发器只做简单、必要的联动(审计、时间戳、简单统计),复杂业务放应用层;
- 触发器逻辑要有日志支撑,便于排查;
- 高写入量的表慎用触发器,注意性能;
- 触发器常用于审计日志、自动更新时间、数据一致性维护。
总结
语法速查
| 操作 | 语法 |
|---|---|
| 创建 | CREATE TRIGGER 名 BEFORE/AFTER INSERT/UPDATE/DELETE ON 表 FOR EACH ROW |
| 查看 | SHOW TRIGGERS |
| 删除 | DROP TRIGGER 名 |
| 访问新旧值 | NEW.列 / OLD.列 |
| 阻止操作 | SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '...' |
关键点
- 触发器由表的 INSERT/UPDATE/DELETE 自动触发,分 BEFORE(可改 NEW)/AFTER(读 NEW/OLD);
NEW存新值、OLD存旧值:INSERT 只有 NEW,DELETE 只有 OLD,UPDATE 都有;- 典型用途:数据审计、自动更新时间、完整性约束、联动统计;
- 触发器隐式执行,注意性能与可排查性,复杂逻辑优先应用层;
- 与存储过程的区别:触发器由事件自动触发、不能带参数调用;存储过程用
CALL显式调用。