触发器中禁止COMMIT/ROLLBACK,状态校验必须用BEFORE UPDATE触发器配合SIGNAL抛出异常;仅允许枚举合法状态跃迁路径,status字段须用ENUM或TINYINT+CHECK约束,严禁在触发器中查其他表或记日志。

触发器里不能 COMMIT,状态校验必须用 BEFORE + SIGNAL
MySQL 触发器中明确禁止 COMMIT、ROLLBACK、START TRANSACTION,所以你没法在触发器里“手动回滚状态变更”。想拦截非法状态跳转,唯一可靠方式是用 BEFORE UPDATE 触发器配合 SIGNAL 抛出异常,让整个 UPDATE 语句失败。
常见错误现象:ERROR 1305 (42000): SAVEPOINT does not exist 或直接语法报错——只要触发器体里出现事务控制语句就会炸。
- 只在
BEFORE UPDATE中做校验,用OLD.status和NEW.status判断是否属于允许的跃迁路径 - 合法路径必须显式枚举,比如
WHEN OLD.status = 0 AND NEW.status = 1,别写模糊条件如NEW.status > OLD.status - 一旦不匹配,用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid status transition'中断执行 - 应用层需捕获这个错误,并区分是业务校验失败还是数据库连接异常
状态字段必须是 ENUM 或带 CHECK 约束的整型,不能是 VARCHAR
如果 status 是 VARCHAR,哪怕触发器里写了 OLD.status = 'pending',也可能因大小写、空格、拼写变体('pendng')导致校验失效。数据库不会帮你归一化。
推荐方案:用 ENUM('draft','pending','approved','rejected'),或 TINYINT 配合 CHECK (status IN (-1,0,1,2,3))(MySQL 8.0.16+ 支持)。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
ENUM能强制值域,且存储紧凑;但新增状态需ALTER TABLE ... MODIFY COLUMN,适合状态极少变动的场景 -
TINYINT + CHECK更灵活,后期加状态只需改代码常量和字段注释,不用动表结构 - 无论哪种,都必须在触发器的
CASE或IF分支里完整覆盖所有允许的(from → to)组合,漏一条就等于开后门
别在触发器里查其他表,尤其不能 JOIN 订单关联表
触发器执行时锁住当前行,如果里面再跑 SELECT FROM users WHERE id = NEW.user_id,不仅可能引发死锁,还会把单次 UPDATE orders 的耗时从 1ms 拉到 100ms+,日均万级写入时直接拖垮吞吐。
典型性能陷阱:为判断“只有管理员能设为 approved”,在触发器里查 roles 表——但 operator_type 和 operator_id 应该由应用层写入主表,而不是让触发器反查。
- 所有前置业务规则(如权限、时间窗、金额匹配)必须在应用层完成校验,再把确定合法的
NEW.status值传进来 - 触发器只做一件事:确认
OLD.status → NEW.status这个跃迁本身是否被允许 - 真要依赖外部数据,提前把必要字段冗余进订单表(如
creator_role),确保触发器内可直接用NEW.creator_role判断
日志记录必须放 AFTER 触发器,且不能依赖触发器保证一致性
有人想用 AFTER UPDATE 触发器自动往 order_status_logs 插日志,这看似省事,但存在两个硬伤:一是日志插入失败会导致整个 UPDATE 失败(而你只想记录,不想阻断主流程);二是触发器无法跨库写日志表,运维上难隔离。
更稳妥的做法是:应用层用事务包裹 UPDATE orders 和 INSERT INTO order_status_logs,靠数据库事务保证二者原子性。
-
AFTER触发器只适合轻量、非关键操作,比如更新统计缓存字段、触发通知队列 ID - 状态日志是审计刚需,必须由应用层控制,确保字段完整(
operator_id、remark等)且可回溯上下文 - 如果坚持用触发器记日志,至少加上
INSERT IGNORE和重试逻辑,并监控日志表写入失败率
UPDATE ... SET status = -1;触发器只管接住这个 -1 并确认它能否从当前状态跳过来。把业务语义判断全丢给触发器,等于把复杂度锁死在数据库里,后期根本没法调试。

















