for each row 和 for each statement 本质区别在于触发时机粒度与上下文可见性:前者每行执行一次,可访问 new/old,适合行级操作;后者整条语句执行后仅运行一次,无 new/old,适用于全局动作。

触发器里 FOR EACH ROW 和 FOR EACH STATEMENT 本质不是“行 vs 语句”那么简单
它们决定的是触发器体执行的**时机粒度**和**上下文可见性**。用错一个,轻则逻辑错乱,重则查不到 NEW/OLD 值、报错或性能崩掉。
FOR EACH ROW 触发器:每改一行就跑一次,能访问 NEW 和 OLD
这是最常用的类型,适合做行级校验、审计日志、自动填充字段。但要注意:它会在单条 UPDATE 影响 10 万行时执行 10 万次——不是 1 次。
- 必须用
BEFORE或AFTER修饰,比如BEFORE INSERT FOR EACH ROW -
NEW在INSERT/UPDATE中可用,OLD在UPDATE/DELETE中可用;INSERT里没OLD,DELETE里没NEW - 不能在
FOR EACH ROW触发器里修改当前正在处理的行(比如在BEFORE UPDATE里再UPDATE同一张表),会引发递归或死锁 - PostgreSQL 支持,MySQL 8.0+ 支持,SQLite 不支持(只有
FOR EACH ROW且不可选)
FOR EACH STATEMENT 触发器:整条 SQL 执行完才跑一次,看不到 NEW/OLD
它不关心改了几行,只关心“这条语句执行完了”。适合做统计更新、权限检查、发通知这类全局动作。但代价是——你拿不到具体哪行变了。
- 没有
NEW和OLD,因为没“当前行”概念;想记录变更,得自己查pg_trigger_depth()或临时表缓存 - 可以安全地在触发器里操作其他表(包括被触发的表本身),不会引发行级冲突
- MySQL 不支持这个语法;PostgreSQL 从 12 开始支持;Oracle 用
FOR EACH STATEMENT类似语法,但叫STATEMENT级触发器 - 如果同时定义了
FOR EACH ROW和FOR EACH STATEMENT,执行顺序是:语句前 → 行前 ×N → 行后 ×N → 语句后
容易踩的坑:混用场景 + 版本兼容性断层
最常出问题的是写了个依赖 NEW.id 的审计逻辑,却用了 FOR EACH STATEMENT,结果运行时报 column "new" does not exist;或者在 MySQL 上写了 FOR EACH STATEMENT,直接语法错误。
- 别假设所有数据库都支持两者:SQLite 只有行级;MySQL 目前只支持行级;PostgreSQL 是目前唯一稳定支持两者的主流引擎
- 不要在
FOR EACH STATEMENT里试图循环遍历变更行——它真没提供接口;要这么做,得回退到FOR EACH ROW+ 临时表聚合 - 同一张表上多个触发器共存时,
FOR EACH ROW的执行顺序由创建顺序决定(PostgreSQL),而FOR EACH STATEMENT总是最后执行,这点容易误判执行时序
FOR EACH STATEMENT 这种语法几乎必然卡住。










