for each row 按行触发,每次访问 new/old 值;for each statement 按语句触发,仅执行一次且不可用 new/old。前者用于行级校验,后者适合汇总统计;mysql 仅支持 row 级,跨库迁移需注意语法兼容性。

FOR EACH ROW 和 FOR EACH STATEMENT 是 SQL 触发器中控制触发频率的核心选项,本质区别在于“按行执行”还是“按语句执行”。选错会导致逻辑错误或性能问题,尤其在批量操作时更明显。
触发时机与执行次数不同
FOR EACH ROW 触发器在每一条受影响的记录上单独执行一次。比如 UPDATE users SET status = 'active' WHERE id IN (1,2,3,4,5) 会触发 5 次,每次可通过 NEW 和 OLD 访问当前行的修改前/后值。
FOR EACH STATEMENT 触发器在整个 SQL 语句执行完毕后只运行一次,不管影响多少行。它不提供行级上下文,无法直接读取某一行的旧值或新值,适合做统计、日志汇总、权限校验等全局性动作。
能否访问行数据是关键分水岭
- FOR EACH ROW 支持使用 NEW.column_name(INSERT/UPDATE 新值)、OLD.column_name(UPDATE/DELETE 旧值),可读可写(BEFORE 类型还可修改 NEW 值)
- FOR EACH STATEMENT 不能使用 NEW 或 OLD,尝试引用会报错;若需行数据,必须显式通过子查询或临时表获取
- 约束触发器(如 CHECK、UNIQUE 相关)强制要求 FOR EACH ROW,因为完整性校验必须逐行进行
性能与适用场景差异明显
- 大批量 INSERT/UPDATE(如导入 10 万行)用 FOR EACH ROW 可能带来显著开销,每行都调用一次函数、查表、发通知,容易拖慢主事务
- 日志记录、审计计数、更新汇总表(如“订单总数”)这类无需逐行细节的操作,优先选 FOR EACH STATEMENT,效率高且逻辑清晰
- 默认行为是 FOR EACH STATEMENT —— 如果建触发器时没写这个子句,就按语句级执行,这点常被忽略
语法与兼容性注意点
PostgreSQL、openGauss、Oracle 都支持两者,但 MySQL 的触发器仅支持 FOR EACH ROW(其触发器天然就是行级)。SQL Server 的 DML 触发器默认也是语句级,但不显式声明 FOR EACH STATEMENT,而是通过触发器内部逻辑判断是否需要遍历 inserted/deleted 表来模拟行级行为。
跨数据库迁移触发器时,需特别检查这一项:PostgreSQL 中写成 FOR EACH STATEMENT 的审计触发器,搬到 MySQL 就会失效,因为 MySQL 不识别该语法。










