1. 先理解触发器:挂在表上的事件驱动程序
很多人一听到"触发器(Trigger)"就下意识觉得这是个高阶特性,觉得平时写 SQL 根本用不到。实际上我在生产环境里见过太多因为没用触发器而搞出来的各种事故:应用层先更新订单状态、再扣库存、再写日志,结果中间某一步服务重启了,订单状态改了但库存没扣,两边数据对不上。这种问题用触发器处理,反而是最简单、最不容易出错的方案。
1.1 一个真实场景:为什么普通 SQL 约束搞不定库存扣减
先说清楚触发器的定位。数据库里绝大多数需求,能用约束(Constraint)解决的就用约束,比如主键、外键、唯一键、CHECK 约束。但约束有个天生的局限:它只能约束"单行数据本身是否合法",没法在一条数据变化后,自动去影响或者校验另外一张表的数据。
举个例子,订单明细表插入了一条购买记录,库存表里对应的商品库存要扣减。这个逻辑:
- 用
CHECK约束?不行,它检查不了别的表。 - 用外键级联?也不行,外键级联只处理引用关系的删除和更新,不会帮你做数量计算。
- 用应用层代码?可以,但依赖应用层一定会在事务里正确执行,一旦应用层有分支漏了,或者有团队直接用 SQL 往库里刷数据,库存就错了。
这种"我是你爸爸,我可以等你有了孩子再给你俩红包"的需求,就是触发器的主战场。触发器就是一段由数据库自动执行的程序,它挂在某张表上,当这张表发生 INSERT、UPDATE、DELETE 时自动运行,不需要你上层的任何代码去显式调用。
提示:触发器不算约束,但它可以在约束扛不住的时候,把业务规则强行压回数据库这一层去执行。
1.2 触发器的执行时机:AFTER、BEFORE 与 INSTEAD OF
触发器最核心的设计维度是"什么时候执行"。
AFTER(也叫FOR):原始操作已经执行完,数据已经写进磁盘(或事务日志),再跑触发器。适合做后续的日志记录、汇总表更新、级联同步。BEFORE:原始操作还没落库,先跑触发器。适合做数据校验、给默认值加工。MySQL 支持BEFORE,Oracle 支持BEFORE,SQL Server 没有BEFORE,它只有AFTER和INSTEAD OF。INSTEAD OF:原始操作干脆不执行,完全由触发器里的代码代替。适合做视图上的写入操作、以及一些复杂的拦截场景,比如"这个订单已经发货了,不允许再改数量"。
这三个时机不是数据库厂商随便设计的,它决定了你在触发器里能干嘛。BEFORE 像安检闸机,先进去检查一遍,不合格直接拦下;AFTER 像收银小票打印机,买卖做完了,自动把小票打出来;INSTEAD OF 像个门卫,进了大门后,你的原计划被替换成门卫安排的新路线。
理解了这个,后面所有代码演示你都不会糊涂。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 触发器的三大分类与不同数据库的语法差异
触发器的类型划分,既有按触发事件分的,也有按数据库实现分的。实际开发里最常打交道的是 DML 触发器,但另外两种也值得了解,遇到时才知道怎么处理。
2.1 DML 触发器:业务中使用频率最高的触发器
DML 触发器就是针对 INSERT、UPDATE、DELETE 三类数据操作设计的触发器。它的编写逻辑通常围绕"新旧值对比"展开。
各数据库对"操作前的旧值、操作后的新值"的表示方式不一样,这是新手最容易搞混的地方:
| 数据库 | 新行引用 | 旧行引用 | 触发级别 |
|---|---|---|---|
| SQL Server | inserted 表 |
deleted 表 |
语句级(Statement-level) |
| MySQL | NEW.列名 |
OLD.列名 |
行级(Row-level) |
| Oracle | :NEW.列名 |
:OLD.列名 |
行级(Row-level) |
| PostgreSQL | NEW.列名 |
OLD.列名 |
行级(Row-level) |
SQL Server 用的是逻辑表 inserted 和 deleted,不是单值。哪怕你只更新了一行,你也要把它当成一张"可能包含多行"的表来对待。而 MySQL、Oracle、PostgreSQL 则是一行一行地触发,FOR EACH ROW 意味着每一行都执行一次触发器体。
这个问题我后面会专门花一章讲,因为大量触发器 Bug 都是在这里翻的车。
2.2 DDL 触发器和登录触发器:很多人没听说过但很有用
除了 DML,SQL Server 还支持 DDL 触发器,比如 FOR CREATE_TABLE、FOR ALTER_TABLE、FOR DROP_TABLE。它能监控库结构变更,一旦有人改了表结构,自动记录或者直接回滚。这在多人协作的数据库里特别有用,能防止有人直接在线上去改生产表结构。
我有个习惯,在核心库里加一个 DDL 触发器,凡是 ALTER_TABLE、DROP_TABLE 直接写进结构变更审计表,并阻止高危操作。这样一旦出问题,我能立刻知道是谁在什么时间改了哪张表。
登录触发器(Logon Trigger)是 SQL Server 特有的,它在用户建立会话时触发,可以用来限制某些账号只能从指定 IP 登录,或者限制并发连接数。这个用得少,但遇到安全合规需求时是个不错的兜底手段。
2.3 SQL Server、MySQL、Oracle 语法差异对照
同样的需求,三种数据库写法差异非常大,不要拿着一份 SQL Server 的触发器脚本往 MySQL 里贴。
| 能力项 | SQL Server | MySQL | Oracle |
|---|---|---|---|
BEFORE 支持 |
不支持 | 支持 | 支持 |
AFTER 支持 |
支持(写作 AFTER 或 FOR) |
支持 | 支持 |
INSTEAD OF 支持 |
支持 | 支持(仅视图) | 支持 |
| 修改触发器 | ALTER TRIGGER |
` |
