触发器:数据库的"自动响应"机制

简介: 数据库触发器是“自动响应”机制:当INSERT/UPDATE/DELETE发生时,无需调用即执行预设逻辑。适用于审计日志、数据校验、自动填充、级联操作等场景。支持BEFORE(可修改数据)和AFTER(常用于记录)两种时机,但需警惕性能影响与调试难度。

💡 触发器 = 数据库的"自动触发器",事件发生时自动执行预定义操作!

大家好呀!我是数据库小学妹👋

昨天我们学了存储过程,可以把一堆SQL打包,然后手动 CALL 调用。但今天遇到了一个自动化需求👇

我想自动记录“每次有人修改订单表,就把旧值和新值存到日志表” —— 难道每次更新都要手动调用一个存储过程吗?万一忘写了怎么办?

数据库早就想到了这个问题,它提供了一种 “自动触发” 的机制——触发器。就像你设置的闹钟,到了点自动响,不用你去按。

今天我就把自己学会的触发器分享出来,保证你看完也能让数据库帮你“自动干活”。

一、什么是触发器?

触发器是一种特殊的存储过程,它在特定的数据库操作(INSERT、UPDATE、DELETE)发生时自动触发执行,无需手动调用。

💡 类比:你养了一只猫,每次它跳上桌子(事件),你就喷水(动作)。触发器就是这种“当A发生时,自动做B”的规则。

✅ 触发器的优点

  • 自动化:事件发生时自动执行
  • 数据一致性:强制执行业务规则
  • 审计追踪:自动记录数据变更
  • 业务逻辑封装:复杂逻辑在数据库层实现
  • 安全性:限制非法数据操作

⚠️ 触发器的限制

  • 调试困难:不像存储过程容易调试
  • 性能影响:触发器执行会增加操作时间
  • 维护成本:逻辑分散在数据库和应用层
  • 不可见性;应用层不知道触发器存在
  • 递归触发:触发器可能触发其他触发器

二、触发器实战:三步创建触发器

🎯 基础语法

-- 创建触发器
CREATE TRIGGER 触发器名
BEFORE/AFTER INSERT/UPDATE/DELETE ON 表名
FOR EACH ROW
BEGIN
    -- 触发器逻辑
END;
-- 查看触发器
SHOW TRIGGERS;
-- 删除触发器
DROP TRIGGER 触发器名;

💡 完整示例

-- 创建测试表
CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT,
    product_id INT,
    amount DECIMAL(10, 2),
    status VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
CREATE TABLE order_audit_log (
    id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT,
    action VARCHAR(20),  -- create/update/delete
    old_status VARCHAR(20),
    new_status VARCHAR(20),
    operator VARCHAR(50),
    action_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- ========================
-- 方案1:没有触发器(每次都要手动记录)
-- ========================
-- 创建订单
INSERT INTO orders (user_id, product_id, amount, status) 
VALUES (1, 101, 2999.00, 'pending');
-- 手动记录日志
INSERT INTO order_audit_log (order_id, action, old_status, new_status, operator) 
VALUES (LAST_INSERT_ID(), 'create', NULL, 'pending', 'admin');
-- ========================
-- 方案2:有触发器(自动记录,无需手动)
-- ========================
-- 1. 创建订单创建触发器
DELIMITER $$
CREATE TRIGGER trg_order_after_insert
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
    INSERT INTO order_audit_log (order_id, action, old_status, new_status, operator)
    VALUES (NEW.id, 'create', NULL, NEW.status, 'system');
END$$
DELIMITER ;
-- 2. 创建订单更新触发器
DELIMITER $$
CREATE TRIGGER trg_order_after_update
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
    IF OLD.status != NEW.status THEN
        INSERT INTO order_audit_log (order_id, action, old_status, new_status, operator)
        VALUES (NEW.id, 'update', OLD.status, NEW.status, 'system');
    END IF;
END$$
DELIMITER ;
-- 3. 创建订单删除触发器
DELIMITER $$
CREATE TRIGGER trg_order_before_delete
BEFORE DELETE ON orders
FOR EACH ROW
BEGIN
    INSERT INTO order_audit_log (order_id, action, old_status, new_status, operator)
    VALUES (OLD.id, 'delete', OLD.status, NULL, 'system');
END$$
DELIMITER ;
-- 测试:现在只需要操作orders表,日志自动记录!
INSERT INTO orders (user_id, product_id, amount, status) 
VALUES (2, 102, 1999.00, 'pending');
UPDATE orders SET status = 'paid' WHERE id = 1;
DELETE FROM orders WHERE id = 1;
-- 查看审计日志
SELECT * FROM order_audit_log;

三、触发器的类型和时机

📊 触发器类型

📊 触发时机

💡 时机选择示例

DELIMITER $$
-- BEFORE INSERT:自动填充创建时间
CREATE TRIGGER trg_user_before_insert
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
    SET NEW.created_at = NOW();
    SET NEW.created_by = 'system';
END$$
-- AFTER INSERT:记录日志
CREATE TRIGGER trg_user_after_insert
AFTER INSERT ON users
FOR EACH ROW
BEGIN
    INSERT INTO user_audit_log (user_id, action, action_time)
    VALUES (NEW.id, 'create', NOW());
END$$
-- BEFORE UPDATE:记录修改时间
CREATE TRIGGER trg_user_before_update
BEFORE UPDATE ON users
FOR EACH ROW
BEGIN
    SET NEW.updated_at = NOW();
    SET NEW.updated_by = 'system';
END$$
-- AFTER UPDATE:记录变更详情
CREATE TRIGGER trg_user_after_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    IF OLD.email != NEW.email THEN
        INSERT INTO user_change_log (user_id, field_name, old_value, new_value, change_time)
        VALUES (NEW.id, 'email', OLD.email, NEW.email, NOW());
    END IF;
END$$
DELIMITER ;

四、触发器的实际应用场景

🎯 场景1:自动填充字段

DELIMITER $$
-- 自动填充创建和修改时间
CREATE TRIGGER trg_auto_timestamp
BEFORE INSERT ON any_table
FOR EACH ROW
BEGIN
    SET NEW.created_at = NOW();
    SET NEW.updated_at = NOW();
END$$
CREATE TRIGGER trg_auto_update_timestamp
BEFORE UPDATE ON any_table
FOR EACH ROW
BEGIN
    SET NEW.updated_at = NOW();
END$$
DELIMITER ;

🎯 场景2:数据验证

DELIMITER $$
-- 验证订单金额不能为负数
CREATE TRIGGER trg_order_before_insert_validate
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
    IF NEW.amount < 0 THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '订单金额不能为负数!';
    END IF;
    
    IF NEW.amount = 0 THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '订单金额不能为0!';
    END IF;
END$$
-- 验证邮箱格式
CREATE TRIGGER trg_user_before_insert_email
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
    IF NEW.email NOT REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$' THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '邮箱格式不正确!';
    END IF;
END$$
DELIMITER ;

🎯 场景3:级联操作

DELIMITER $$
-- 删除用户时,级联删除相关数据
CREATE TRIGGER trg_user_before_delete_cascade
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
    -- 删除用户的订单
    DELETE FROM orders WHERE user_id = OLD.id;
    
    -- 删除用户的评论
    DELETE FROM comments WHERE user_id = OLD.id;
    
    -- 删除用户的收藏
    DELETE FROM favorites WHERE user_id = OLD.id;
END$$
-- 更新产品时,同步更新相关表
CREATE TRIGGER trg_product_after_update_sync
AFTER UPDATE ON products
FOR EACH ROW
BEGIN
    -- 更新订单中的产品名称(如果产品名称变了)
    IF OLD.name != NEW.name THEN
        UPDATE order_items 
        SET product_name = NEW.name 
        WHERE product_id = NEW.id;
    END IF;
END$$
DELIMITER ;

🎯 场景4:业务规则强制执行

DELIMITER $$
-- 库存不足时,禁止创建订单
CREATE TRIGGER trg_order_before_insert_stock_check
BEFORE INSERT ON orders
FOR EACH ROW
BEGIN
    DECLARE available_stock INT;
    
    SELECT stock INTO available_stock 
    FROM products 
    WHERE id = NEW.product_id;
    
    IF available_stock < NEW.quantity THEN
        SIGNAL SQLSTATE '45000' 
        SET MESSAGE_TEXT = '库存不足,无法创建订单!';
    END IF;
    
    -- 扣减库存
    UPDATE products 
    SET stock = stock - NEW.quantity 
    WHERE id = NEW.product_id;
END$$
-- 订单完成后,自动增加用户积分
CREATE TRIGGER trg_order_after_update_points
AFTER UPDATE ON orders
FOR EACH ROW
BEGIN
    IF OLD.status != 'completed' AND NEW.status = 'completed' THEN
        UPDATE users 
        SET points = points + FLOOR(NEW.amount / 100) 
        WHERE id = NEW.user_id;
    END IF;
END$$
DELIMITER ;

🎯 场景5:数据审计和追踪

DELIMITER $$
-- 全面的用户操作审计
CREATE TRIGGER trg_user_full_audit
AFTER INSERT ON users
FOR EACH ROW
BEGIN
    INSERT INTO user_audit (user_id, action, details, audit_time)
    VALUES (
        NEW.id, 
        'INSERT', 
        CONCAT('创建用户: ', NEW.username, ', 邮箱: ', NEW.email),
        NOW()
    );
END$$
CREATE TRIGGER trg_user_update_audit
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    DECLARE changes TEXT DEFAULT '';
    
    IF OLD.username != NEW.username THEN
        SET changes = CONCAT(changes, '用户名: ', OLD.username, ' -> ', NEW.username, '; ');
    END IF;
    
    IF OLD.email != NEW.email THEN
        SET changes = CONCAT(changes, '邮箱: ', OLD.email, ' -> ', NEW.email, '; ');
    END IF;
    
    IF OLD.status != NEW.status THEN
        SET changes = CONCAT(changes, '状态: ', OLD.status, ' -> ', NEW.status, '; ');
    END IF;
    
    IF changes != '' THEN
        INSERT INTO user_audit (user_id, action, details, audit_time)
        VALUES (NEW.id, 'UPDATE', changes, NOW());
    END IF;
END$$
CREATE TRIGGER trg_user_delete_audit
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
    INSERT INTO user_audit (user_id, action, details, audit_time)
    VALUES (
        OLD.id, 
        'DELETE', 
        CONCAT('删除用户: ', OLD.username, ', 邮箱: ', OLD.email),
        NOW()
    );
END$$
DELIMITER ;

五、触发器与存储过程的区别

一句话总结

  • 需要“自动响应” → 触发器
  • 需要“主动调用” → 存储过程

六、新手避坑指南(血泪总结😭)

七、今日学习心得

今天的内容总结成三句话:

  1. 触发器是自动执行的,当表发生 INSERT/UPDATE/DELETE 时触发
  2. BEFORE 用于校验和修改,AFTER 用于记录日志和同步
  3. 适合审计、校验、自动同步,但别滥用,避免性能陷阱

👋 我是数据库小学妹一个用设计师思维学数据库的转行人。我们一起,把复杂的技术变得简单有趣!💕


本文为个人学习总结,所有命令均在MySQL 8.0环境下验证。触发器虽方便,但请谨慎使用,保持数据库逻辑清晰。


相关文章
|
4月前
|
SQL 关系型数据库 MySQL
主键、外键和约束:让数据库“有规矩”才能不出错!|转行学DB第5天
本文用通俗易懂的语言讲解了主键(数据的唯一标识)、外键(表间关联)以及唯一约束、非空约束等其他常见约束规则。通过具体SQL示例展示了各种约束的使用方法,并分享了新手容易踩的坑和实用建议。
|
4月前
|
人工智能 监控 Kubernetes
LoongCollector + ACS Agent Sandbox:构建 AI Agent 生产级运行平台
文章介绍了阿里云ACSAgentSandbox与LoongCollector协同构建的AIAgent生产级运行平台,通过沙箱隔离保障运行时安全,并以高性能、全链路可观测能力解决Agent行为不可预测和执行风险难题。
2352 78
|
4月前
|
SQL 关系型数据库 MySQL
数据量大查询慢?索引让你的SQL秒级响应!|转行学DB第9天
用生活化比喻(如字典目录)详解索引原理:它通过B+树结构加速查询,避免全表扫描;涵盖创建、查看、删除索引方法,联合索引的最左前缀原则,以及读写平衡等实战要点——让查询从“等几秒”变“秒出”!
数据量大查询慢?索引让你的SQL秒级响应!|转行学DB第9天
|
4月前
|
SQL 关系型数据库 MySQL
5款好用的免费MySQL客户端,新手必备!
告别枯燥命令行!数据库小学妹精选5款免费MySQL图形化工具:Workbench(官方全能)、phpMyAdmin(免安装Web版)、DBeaver(多库支持)、HeidiSQL(Windows轻量之选)、TablePlus(高颜值跨平台)。小白友好,语法高亮、自动补全、可视化结构一应俱全,助你高效学SQL!
|
2月前
|
SQL 关系型数据库 MySQL
SQL代码审查指南:命名规范+10大反模式+四维检查清单,一篇全搞定
数据库小学妹带你攻克SQL规范难题!从命名、格式到10大反模式(如SELECT*、隐式转换、ORDER BY RAND等),结合真实踩坑案例,详解可读、可维护、高性能的SQL写法,并提供SQL Review四维审查清单与团队落地方法,助你写出工业级质量SQL。
|
2月前
|
存储 运维 关系型数据库
分布式数据库架构演进:从集中式到分布式,三大路线一次讲清楚
本文深入浅出解析分布式数据库选型逻辑:厘清集中式与分布式适用边界,剖析分片、事务、一致性三大技术难点,对比分库分表、原生分布式、共享存储集群三类架构。务实提醒——分布式非银弹,够用才是硬道理。
|
2月前
|
SQL 关系型数据库 MySQL
MySQL慢查询治理最佳实践:从定位到优化再到上线的完整方案
本文系统讲解MySQL慢查询排查与优化全流程:从`SHOW PROCESSLIST`快速定位问题,到`pt-query-digest`精准识别高耗时SQL;结合`EXPLAIN`深度分析执行计划(重点关注type、Extra、rows);覆盖六大高频场景(深分页、SELECT*、COUNT(*)、多表JOIN等)的实战优化方案;并强调生产规范——大表加索引用pt-osc、上线前必验执行计划、关键参数调优要点。重流程、轻工具,助你从容应对线上性能危机。(239字)
|
4月前
|
SQL 安全 关系型数据库
MySQL避坑指南:从逻辑备份到物理备份,新手必看的救命稻草。
数据库小学妹带你轻松应对误删!用`mysqldump`逻辑备份+`mysql`命令快速恢复,安全、简单、零门槛——备份不是可选项,而是DBA的保命符!
|
2月前
|
JSON 关系型数据库 MySQL
MySQL JSON 类型生产实战:部分更新、虚拟列索引、性能边界与 Schema 设计决策
MySQL 8.0 的 JSON 类型不是摆设,很多场景比拆子表好用太多。本文从 JSON 类型和 TEXT 存 JSON 的本质区别讲起,覆盖 JSON 函数速查、虚拟列索引方案、三大实战场景、5 个常见坑,以及什么时候该老老实实建子表。附面试话术+避坑清单