mysql 8.0 不支持 follows 语法,该子句非官方合法语法,任何版本执行均报 error 1064;官方文档未收录,show create trigger 永不显示,实际执行顺序仅参考 created 时间戳但不保证,唯一可靠方案是合并为单个触发器并显式控制逻辑。

MySQL 8.0 真的不支持 FOLLOWS 语法
直接说结论:FOLLOWS 和 PRECEDES **不是 MySQL 的合法语法**,哪怕你查到某些博客、旧文档或建模工具里写了这种写法,执行时一定会报错。MySQL 官方文档(包括 8.0.33+ 最新版)从未将它们纳入 CREATE TRIGGER 语法;SHOW CREATE TRIGGER 输出中也**永远看不到这两个关键字**。所谓“MySQL 8.0+ 支持 FOLLOWS”是广泛流传的误解,根源可能是混淆了 MariaDB(它确实支持)或误读了早期 RFC 草案。
为什么 SHOW CREATE TRIGGER 里找不到 FOLLOWS?
因为 MySQL 根本不解析它——你写进去就会被拒绝:
CREATE TRIGGER tr_b BEFORE INSERT ON t FOR EACH ROW FOLLOWS tr_a BEGIN ... END;
这条语句在任何正式版 MySQL(5.7/8.0/8.1)上都会触发类似 ERROR 1064 (42000) 的语法错误。MySQL 解析器只认标准语法:事件类型(INSERT/UPDATE/DELETE)、时机(BEFORE/AFTER)、表名、FOR EACH ROW 和主体逻辑。任何额外子句(包括 FOLLOWS、TRIGGER_ORDER、PRIORITY)都不被接受。
常见错误现象:
- 用 Navicat 或 DBeaver 导出触发器时看到
FOLLOWS—— 那是工具自作主张加的,不是 MySQL 写进去的 - 从 MariaDB 迁移脚本到 MySQL 失败 —— 因为 MariaDB 支持
FOLLOWS,但 MySQL 不兼容 - 执行
SHOW CREATE TRIGGER tr_x;返回结果里没有FOLLOWS字样 —— 这是正常且唯一的事实
那怎么确认当前多个 BEFORE INSERT 触发器谁先执行?
只能依赖 INFORMATION_SCHEMA.TRIGGERS.CREATED 字段(MySQL 5.7+ 才有),但它只是“参考”,不是保证:
-
CREATED是触发器创建完成的时间戳,升序排列通常对应执行顺序 - 但
DROP TRIGGER + CREATE TRIGGER会重置CREATED,顺序可能翻转 - 主从复制中,从库重建触发器的时间可能和主库不同,导致主从执行顺序不一致
-
SHOW TRIGGERS输出顺序 ≠ 执行顺序,它只是按CREATED或内部 ID 排的查询结果
真正验证顺序的方法只有实测:在每个触发器里加调试语句,比如:
SELECT CONCAT('tr_validate fired at ', NOW());
然后执行一条 INSERT,看输出顺序。但这不能用于生产环境,且无法解决逻辑耦合问题。
替代方案:合并触发器才是唯一可控路径
当业务需要「先校验 → 再设默认值 → 最后生成状态码」这类明确流程时,不要拆成 tr_validate、tr_defaults、tr_status 三个触发器。MySQL 不提供任何机制保证它们按需串行,强行拆分等于把顺序交给不可控的创建时间。
正确做法是写一个触发器,把逻辑按顺序组织:
BEGIN
-- [VALIDATE] 检查 email 格式
IF NEW.email NOT REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid email';
END IF;
<p>-- [DEFAULTS] 补全缺失字段
IF NEW.created_at IS NULL THEN
SET NEW.created_at = NOW();
END IF;
IF NEW.status IS NULL THEN
SET NEW.status = 'pending';
END IF;</p><p>-- [STATUS] 生成业务状态码
SET NEW.code = CONCAT('USR-', UNIX_TIMESTAMP(), '-', FLOOR(RAND() * 1000));
END</p>
这样所有行为都发生在同一上下文,NEW 的修改对后续语句立即可见,无竞态、无依赖盲区、主从一致。
容易被忽略的关键点:多个同类型触发器不仅顺序不确定,还会增加每次 DML 的解析与调用开销;而单个触发器内逻辑虽多,但只编译一次、缓存一次,性能反而更稳。别被“职责分离”的教条带偏——在 MySQL 触发器场景下,**合并不是妥协,是必要控制手段**。











