SQL触发器是数据库在INSERT、UPDATE、DELETE时自动执行的存储过程,用于保障数据一致性、强制业务规则、审计日志等,适用于“数据变更即需响应”的场景,但需规避性能、递归、事务等风险。

SQL触发器是数据库自动执行的特殊存储过程,它在特定数据操作(INSERT、UPDATE、DELETE)发生时被隐式调用,无需手动调用,也不依赖应用程序逻辑。它的核心价值在于保障数据一致性、实现业务约束、记录操作日志或同步关联表,但不适用于复杂事务或跨库操作。
什么时候该用触发器?
触发器适合解决“只要数据变了,就必须立刻做点什么”的场景,且该动作应由数据库层统一控制,避免应用层遗漏:
- 强制业务规则:比如订单状态为“已发货”后,禁止再修改收货地址
- 自动填充/更新字段:插入新用户时自动生成唯一编码、记录创建时间戳
- 审计跟踪:把每次敏感表(如工资表、权限表)的修改写入日志表,含操作人、时间、旧值、新值
- 级联更新或同步:某商品价格变更时,自动更新其所有历史订单明细中的快照价格(注意:非实时关联查询,而是固化当时值)
- 数据校验增强:CHECK约束无法实现的跨表校验(如插入订单前检查客户余额是否充足),可用触发器抛出错误中止操作
触发器类型与触发时机怎么选?
不同数据库略有差异,以MySQL和SQL Server为主流参考:
-
BEFORE INSERT / BEFORE UPDATE / BEFORE DELETE:常用于数据清洗、默认值填充、条件拦截。例如:BEFORE INSERT 可把空邮箱设为'unknown@domain.com',或检查金额是否为负数并SET NEW.amount = ABS(NEW.amount)
-
AFTER INSERT / AFTER UPDATE / AFTER DELETE:适合日志记录、通知类操作、或需依赖刚生成主键ID的后续处理(如插入后往关联表写记录)
- 注意:MySQL不支持FOR EACH STATEMENT级别的触发器(即整条SQL只触发一次),只支持FOR EACH ROW;SQL Server支持AFTER和INSTEAD OF,后者可替代原操作(比如把INSERT转成写入归档表)
写触发器要注意哪些坑?
看似简单,实际容易引发性能、死锁或逻辑错误:
- 避免在触发器里做耗时操作:比如发邮件、调外部API、查大量数据——会拖慢主SQL执行,甚至导致超时
- 慎用递归触发:比如A表UPDATE触发B表UPDATE,而B表也有UPDATE触发器又改了A表,可能无限循环(MySQL默认禁用,SQL Server需显式开启)
- 区分OLD和NEW关键字:UPDATE时OLD代表修改前的行,NEW代表修改后的行;INSERT只有NEW;DELETE只有OLD。误用会导致逻辑翻车
- 事务上下文要清楚:触发器和原SQL同属一个事务,若触发器报错,整个事务回滚;但有些数据库(如MySQL)在触发器中开启新事务会报错
- 不要假设单行操作:即使你平时只INSERT一条,触发器必须按多行设计(如批量导入),否则用NEW.id取ID可能出错
一个实用例子:订单状态变更审计
目标:当orders表status字段被修改时,自动记录到order_status_log表,含订单号、旧状态、新状态、操作时间、操作人(从SESSION_CONTEXT或应用传入字段获取):
-- MySQL示例(假设应用通过user_id字段传递操作人)CREATE TRIGGER tr_order_status_auditAFTER UPDATE ON ordersFOR EACH ROWBEGIN IF OLD.status != NEW.status THEN INSERT INTO order_status_log (order_id, old_status, new_status, updated_at, operator_id) VALUES (OLD.id, OLD.status, NEW.status, NOW(), NEW.updated_by); END IF;END;基本上就这些。触发器不是银弹,用对地方能省心,滥用反而埋雷。关键是想清楚:这事是不是必须由数据库兜底?有没有更轻量的方式(如应用层统一Service方法)?权衡之后再动手。
以上就是SQL触发器使用详细说明_SQL TRIGGER应用场景解析的详细内容,更多请关注php中文网其它相关文章!