mysql菜鸟教程

专栏导航

16.3 触发时机:BEFORE与AFTER

在创建触发器时,必须指定触发时机——BEFORE 或 AFTER。这两个关键字决定了触发器是在触发事件之前还是之后执行。理解它们的区别以及各自的适用场景,是合理设计触发器的关键。

一、BEFORE 触发器

BEFORE 触发器 在触发事件(INSERTUPDATEDELETE执行之前被激活。此时,数据尚未写入磁盘(对于 INSERT 和 UPDATE)或尚未被删除(对于 DELETE)。

特点

  • 可以修改即将插入或更新的数据:通过 NEW 关键字,可以在 BEFORE INSERT 和 BEFORE UPDATE 触发器中修改列的值。

  • 可以阻止操作:如果检查到数据不符合业务规则,可以通过 SIGNAL 语句抛出错误,从而阻止事件的执行。

  • 不能访问旧值(OLD)的修改OLD 在 BEFORE UPDATE 和 BEFORE DELETE 中只读,不可修改。

适用场景

  • 数据验证:检查插入或更新的数据是否满足业务规则(如年龄范围、格式校验)。如果不满足,直接抛出错误,阻止无效数据进入数据库。

  • 默认值填充:为某些列自动设置值(例如创建时间、更新时间、自动计算列)。

  • 数据清洗:对输入数据进行规范化,例如去除字符串前后空格、统一大小写等。

二、AFTER 触发器

AFTER 触发器 在触发事件执行完成之后被激活。此时,数据已经写入磁盘(对于 INSERT 和 UPDATE)或已被删除(对于 DELETE)。

特点

  • 不能修改当前操作的数据:因为事件已经完成,数据已定。但可以通过其他 UPDATE 语句修改本表或其他表(注意避免递归)。

  • 主要用于记录操作的结果:例如记录日志、更新汇总表、触发后续处理等。

  • 可以读取 OLD 和 NEW,但无法修改它们。

适用场景

  • 审计日志:记录数据变更的历史,例如将修改前后的值存入日志表。

  • 级联更新:当主表数据变化时,自动更新其他相关表中的数据(但要注意避免循环触发)。

  • 更新统计信息:例如,当订单明细插入后,自动更新订单的总金额。

三、BEFORE 与 AFTER 对比

特性

BEFORE

AFTER

触发时间

事件执行之前

事件执行之后

能否修改当前操作的行(NEW)

可以(仅 INSERT/UPDATE)

不能

能否阻止事件

可以(通过抛出错误)

不能(事件已完成)

对 OLD 的访问

只读

只读

主要用途

验证、计算默认值、数据清洗

记录日志、级联更新、同步数据

性能影响

略微增加操作前的开销

略微增加操作后的开销

示例详解

1. BEFORE INSERT:数据验证与默认值

假设有一个 users 表,要求用户名不能为空,邮箱格式基本正确,并自动设置注册时间。

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    register_time DATETIME
);

DELIMITER //

CREATE TRIGGER before_insert_users
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
    -- 1. 验证用户名不为空
    IF NEW.username IS NULL OR NEW.username = '' THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '用户名不能为空';
    END IF;
    
    -- 2. 简单验证邮箱格式(必须包含 @)
    IF NEW.email IS NOT NULL AND NEW.email NOT LIKE '%@%' THEN
        SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '邮箱格式不正确';
    END IF;
    
    -- 3. 自动设置注册时间(如果未提供)
    IF NEW.register_time IS NULL THEN
        SET NEW.register_time = NOW();
    END IF;
END //

DELIMITER ;

2. BEFORE UPDATE:自动更新时间戳

许多表需要一个“最后更新时间”字段,可以在 BEFORE UPDATE 触发器中自动维护。

ALTER TABLE users ADD COLUMN last_update TIMESTAMP;

DELIMITER //

CREATE TRIGGER before_update_users
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
    SET NEW.last_update = NOW();
END //

DELIMITER ;

3. AFTER INSERT:记录日志

当新用户注册后,自动向日志表插入一条记录。

CREATE TABLE user_log (
    log_id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    action VARCHAR(20),
    log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

DELIMITER //

CREATE TRIGGER after_insert_users
AFTER INSERT ON users
FOR EACH ROW
BEGIN
    INSERT INTO user_log (user_id, action) VALUES (NEW.id, 'INSERT');
END //

DELIMITER ;

4. AFTER UPDATE:记录变更审计

记录用户关键信息(如邮箱)的修改历史。

CREATE TABLE user_audit (
    audit_id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    old_email VARCHAR(100),
    new_email VARCHAR(100),
    change_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

DELIMITER //

CREATE TRIGGER after_update_users
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    IF OLD.email != NEW.email THEN
        INSERT INTO user_audit (user_id, old_email, new_email)
        VALUES (OLD.id, OLD.email, NEW.email);
    END IF;
END //

DELIMITER ;

5. BEFORE DELETE:记录删除前的数据

在删除数据前,将其备份到历史表(但注意:BEFORE DELETE 中还可以访问 OLD,可以用于记录,但数据尚未删除,如果希望在删除后记录,则用 AFTER DELETE)。

CREATE TABLE deleted_users LIKE users;

DELIMITER //

CREATE TRIGGER before_delete_users
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
    INSERT INTO deleted_users SELECT * FROM users WHERE id = OLD.id;
END //

DELIMITER ;

四、选择建议

  • 如果你需要修改即将插入/更新的数据,或者在数据真正写入前进行验证并可能阻止操作,选择 BEFORE

  • 如果你需要在操作完成后记录日志、更新其他表,并且不需要修改当前操作的数据,选择 AFTER

  • 对于同一类操作,可以同时使用 BEFORE 和 AFTER 触发器,分别完成不同阶段的任务。

五、注意事项

  1. 不能对同一张表的同一个触发事件定义多个同一时机的触发器。例如,不能有两个 AFTER INSERT 触发器,但可以有一个 BEFORE INSERT 和一个 AFTER INSERT

  2. 在 BEFORE 触发器中抛出错误,整个操作(包括触发器自身)会回滚。对于 InnoDB 表,即使触发器失败,语句也会回滚。

  3. 避免在 AFTER 触发器中修改同一张表,这可能引发递归调用。例如,在 AFTER UPDATE 中再次 UPDATE 同一行,会再次触发同一个触发器,导致死循环。MySQL 默认禁止递归(max_sp_recursion_depth = 0),但仍有风险。

  4. 性能影响:触发器会增加每次 DML 操作的开销,尤其在高并发场景下,应避免在触发器内执行复杂的查询或长时间操作。

六、小结

  • BEFORE 和 AFTER 决定了触发器在事件之前还是之后执行。

  • BEFORE 适合数据验证、默认值填充、数据清洗;AFTER 适合审计日志、级联更新。

  • BEFORE 触发器可以修改 NEW 的值并阻止操作;AFTER 触发器只能读取,不能修改。

  • 合理选择触发时机,可以让数据库自动完成许多工作,提升数据的一致性和完整性。


所有评论

关于我 备案号:蜀ICP备2023042032号-1