{T}

触发器

触发器(Trigger)是与表关联、在特定事件(INSERT/UPDATE/DELETE)发生时自动执行的一组 SQL 语句。它把"数据变更后的联动处理"固化在数据库层,实现自动审计、自动更新、数据一致性等。

为什么需要触发器

场景触发器的作用
数据审计记录每次增删改的操作日志
自动更新时间更新 updated_at 字段
完整性约束复杂业务规则在数据库层强制
联动更新删订单同时更新用户统计

触发器的好处是自动、不依赖应用调用;代价是隐式执行、难以排查(问题可能来自触发器而非业务代码),且影响写入性能。

触发器的类型

触发时机分:

类型触发时机
BEFORE事件发生前(可修改 NEW 值,常用于校验/补默认值)
AFTER事件发生后(多用于审计、联动更新)

触发事件分:INSERTUPDATEDELETE

创建触发器

基本语法(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 伪行

触发器里用 NEWOLD 访问变更前后的行:

伪行INSERTUPDATEDELETE
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
不可见性应用层看不到触发逻辑,易产生"意外的数据变化"

最佳实践

  1. 触发器只做简单、必要的联动(审计、时间戳、简单统计),复杂业务放应用层;
  2. 触发器逻辑要有日志支撑,便于排查;
  3. 高写入量的表慎用触发器,注意性能;
  4. 触发器常用于审计日志、自动更新时间、数据一致性维护

总结

语法速查

操作语法
创建CREATE TRIGGER 名 BEFORE/AFTER INSERT/UPDATE/DELETE ON 表 FOR EACH ROW
查看SHOW TRIGGERS
删除DROP TRIGGER 名
访问新旧值NEW.列 / OLD.列
阻止操作SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '...'

关键点

  1. 触发器由表的 INSERT/UPDATE/DELETE 自动触发,分 BEFORE(可改 NEW)/AFTER(读 NEW/OLD);
  2. NEW 存新值、OLD 存旧值:INSERT 只有 NEW,DELETE 只有 OLD,UPDATE 都有;
  3. 典型用途:数据审计、自动更新时间、完整性约束、联动统计;
  4. 触发器隐式执行,注意性能与可排查性,复杂逻辑优先应用层;
  5. 与存储过程的区别:触发器由事件自动触发、不能带参数调用;存储过程用 CALL 显式调用。