SQL 触发器与事件管理

2次阅读

mysql触发器中禁止使用commit/rollback,因其运行在父语句事务上下文中;事件调度器默认关闭,需手动启用;truncate table绕过所有触发器;Event与trigger均不可跨会话引用临时表。

SQL 触发器与事件管理

MySQL 触发器里不能用 COMMITROLLBACK

触发器运行在父语句的事务上下文中,它本身不是独立事务。一旦你在 BEFOREAFTER 触发器里写 COMMIT,MySQL 直接报错:Error 1305 (42000): function does not exist(实际是语法拒绝,但错误提示常误导人)。

常见错误现象:想在插入后自动更新统计表并提交,结果整个 INSERT 失败;或者误以为加了 START TRANSACTION 就能“另起炉灶”,其实触发器内不允许任何显式事务控制语句。

  • 触发器中只允许 DML(INSERT/UPDATE/delete)、变量赋值、条件判断,不能调用存储过程以外的事务命令
  • 如果真需要跨表一致性操作,把逻辑提到应用层,或改用存储过程封装整套动作
  • INSERT ... ON DUPLICATE KEY UPDATE 配合触发器时尤其危险——冲突路径可能绕过触发器预期执行顺序

MySQL 事件调度器(Event Scheduler)默认是关的

写了 CREATE EVENT 却没反应?大概率是 event_scheduler 系统变量为 OFF。它不像触发器那样随表自动激活,必须手动打开。

使用场景包括定时归档旧数据、每小时刷新汇总视图、清理临时表等。但它不保证精确到秒级执行,且依赖 MySQL 实例持续运行。

  • 检查状态:SHOW VARIABLES LIKE 'event_scheduler';,返回 OFF 就得开
  • 临时开启:SET GLOBAL event_scheduler = ON;(需 SUPER 权限)
  • 永久生效:在 my.cnf[mysqld] 段落加一行 event_scheduler=ON
  • 事件定义里用 ON COMPLETION PRESERVE 可避免执行完就自动删除,方便调试

触发器无法响应 TRUNCATE TABLE

TRUNCATE TABLE 不走 DML 流程,而是直接重建表结构,绕过所有触发器。哪怕你建了 AFTER DELETE 触发器,对 TRUNCATE 完全无效。

这在日志审计、软删除设计、计数器同步等场景容易踩坑——你以为删光了会触发清理逻辑,其实什么都没发生。

  • 替代方案只有两种:改用 DELETE FROM table(注意带 WHERE 1=1 并确认有索引支撑性能)
  • 或在应用层强制拦截 TRUNCATE 命令,统一转成带触发器的 DELETE
  • 某些 ORM 自动生成 TRUNCATE(比如测试环境清库),要特别检查其行为是否被触发器覆盖

EVENT 和 TRIGGER 都不能跨数据库引用临时表

不管是事件里建的临时表,还是触发器里 select 的临时表,只要不是当前会话创建的,就会报 ERROR 1146 (42S02): Table 'db.tmp' doesn't exist。因为临时表只在创建它的会话内可见,而事件在独立线程运行,触发器则依附于调用语句的会话——但都不是“你当前连着的那个”。

这个限制在做分库汇总、中间计算时特别隐蔽,比如想在事件里先算出一批 ID 再批量更新主表,结果临时表根本读不到。

  • 避免用临时表传数据,改用普通表 + 唯一前缀(如 tmp_<code>CONNECTION_ID()_xxx)
  • 触发器中禁止 SELECT ... INTO TEMPORARY TABLE,哪怕只是中间步骤
  • 事件中若需缓存中间结果,优先考虑内存表(ENGINE=MEMORY)或带 TTL 的普通表

事情说清了就结束。触发器和事件看着像“自动干活的工具”,但它们各自卡在事务模型、会话隔离、权限边界这些地方,稍不注意就静默失效。最麻烦的是——出问题时往往没报错,只是逻辑没跑。

text=ZqhQzanResources