触发器是绑定在表上的行级自动执行代码块,INSERT/UPDATE/DELETE逐行触发;BEFORE可读写NEW/OLD并修改数据,AFTER不可修改但可安全操作其他表;禁止同表DML、事务控制及返回结果集。

触发器本质是「自动执行的行级钩子」
MySQL 触发器不是存储过程,也不是定时任务,而是绑定在某张永久表上的、由 INSERT/UPDATE/DELETE 操作**逐行触发**的代码块。只要一条 SQL 影响了 N 行,触发器就执行 N 次 —— 这是它最核心的行为特征,也是绝大多数逻辑错误的源头。
BEFORE 和 AFTER 的关键区别不只是时机
BEFORE 触发器能读写 NEW(插入/更新后)和 OLD(更新/删除前),还能修改 NEW 字段值(比如自动填充 created_at 或校验并拒绝非法数据);AFTER 触发器不能改 NEW 或 OLD,但能安全地操作其他表(比如日志表、统计表),因为主表变更已落地,不会因触发器失败导致主事务回滚失败。
-
BEFORE INSERT:适合默认值填充、字段格式标准化、业务规则拦截(如金额不能为负) -
AFTER INSERT:适合同步写入审计日志、更新关联统计(如“订单数+1”)、调用存储过程做异步通知(通过参数传值) -
BEFORE UPDATE:可防止误改关键字段(如禁止修改user_id),或自动更新updated_at -
AFTER DELETE:常用于软删标记、归档旧数据、清理缓存关联记录
创建时必须绕开三个硬限制
MySQL 触发器有明确的能力边界,踩中任意一条就会报错:ERROR 1442 (HY000): Can't update table 'xxx' in stored function/trigger because it is already used by statement which invoked this stored function/trigger 或权限不足等。
- 不能在触发器里对**同一张表**执行
INSERT/UPDATE/DELETE(会引发递归或死锁) - 不能使用
START TRANSACTION、COMMIT、ROLLBACK—— 事务由外部语句控制,触发器内只能参与,不能干预 - 不能调用返回结果集的存储过程(比如含
SELECT ... INTO但没接变量),也不能用CALL执行带SELECT输出的过程
替代方案:用 INSERT ... SELECT 写其他表;用 SET @var := (SELECT ...) 取单值;把复杂逻辑拆到存储过程中,仅用 OUT 参数传参给触发器。
查看和调试触发器的实际命令
开发中经常要确认触发器是否生效、顺序是否正确、逻辑有没有被跳过。别只靠 SHOW TRIGGERS 看列表,得结合行为验证:
SHOW TRIGGERS LIKE 'account';
更可靠的方式是查系统表(需有 SELECT 权限):
SELECT TRIGGER_NAME, EVENT_MANIPULATION, EVENT_OBJECT_TABLE, ACTION_TIMING, ACTION_STATEMENT FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'your_db_name' AND EVENT_OBJECT_TABLE = 'account';
调试建议:在触发器里加 INSERT INTO debug_log VALUES (NOW(), 't1 fired', NEW.id);(前提是 debug_log 是另一张表),避免用 SELECT 直接输出 —— MySQL 不允许触发器返回结果集。
真正难的从来不是语法,而是想清楚「这行数据变的时候,我到底要动什么、不能动什么、谁该负责回滚」。很多线上事故,都源于在 AFTER 里去改主表,或在 BEFORE 里写了耗时查询拖慢整个事务。










