
触发器适合强制哪些业务规则
只有当规则涉及多表计算、跨行校验、动态条件判断,或需要访问变更前/后的完整数据快照时,才该用触发器。比如「订单总金额不能超过客户信用额度」——这必须联查 orders 和 customers 表,且要对比 INSERTED(新订单)与 DELETED(旧订单)中的金额变化;而单字段非空、唯一性、范围检查这类,应优先用 CHECK 约束或 UNIQUE 约束。
BEFORE vs AFTER:时机选错直接失效
想阻止非法数据写入,必须用 BEFORE INSERT 或 BEFORE UPDATE,并在逻辑中主动抛错;AFTER 触发器无法回滚主操作(除非显式 ROLLBACK,但会中断整个事务)。常见错误是误用 AFTER 做校验,结果非法数据已落库,只在日志里报错。
- MySQL 中用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '信用超限'; - SQL Server 中用
THROW 50000, '信用超限', 1; - PostgreSQL 中用
RAISE EXCEPTION '信用超限';
避免触发器嵌套和死循环
触发器内修改自身关联表,极易引发递归触发。例如在 orders 的 AFTER INSERT 中更新 customers.credit_used,若 customers 上也有触发器,就可能形成链式调用。解决方法:
- SQL Server 可用
TRIGGER_NESTLEVEL()检查当前嵌套深度,大于 1 就直接RETURN - MySQL 无原生控制,需靠命名规范 + 注释明确标注“此触发器禁止修改 orders/customers 表”
- 所有跨表更新逻辑,统一走存储过程封装,触发器只做轻量判断和调用
性能敏感点:别在触发器里查大表或加锁
触发器运行在主 DML 事务内,任何慢查询都会拖慢整个插入/更新。尤其注意:
- 避免在
FOR EACH ROW触发器中对百万级表执行SELECT COUNT(*) - 不要在触发器中调用含
SELECT ... FOR UPDATE的语句,容易造成锁等待甚至死锁 - 审计类触发器若写日志表,建议用异步方式(如写入消息队列),或至少确保日志表无复杂索引和外键
最常被忽略的是触发器的可测试性——它没有参数、不暴露接口,很难单元测试。上线前务必用真实数据量做压测,并确认错误路径(如信用校验失败)是否真能阻断写入,而不是静默吞掉异常。










