WHEN子句仅在行级触发器中生效,且只能引用:new/:old字段和简单表达式;复杂逻辑须移入触发器体,UPDATE OF需配合WHEN使用,禁止在WHEN中调用函数、绑定变量或会话函数。
WHEN子句写法直接影响触发器是否执行
pl/sql触发器的 when 子句不是简单过滤,它在触发器体执行前就做判断,且只支持行级触发器(for each row)。如果写成 when (new.status = 'processed'),数据库会为每一行都求值——但注意:它不能引用函数、子查询或包变量,只能用 :new / :old 伪记录字段和简单表达式。
- 常见错误:在
WHEN中调用自定义函数,如WHEN (my_pkg.is_valid(:new.id))→ 报错PLS-00436: implementation restriction: cannot reference fields of BULK In-BIND table of records或直接不支持 - 正确做法:把复杂逻辑移到触发器体内部,
WHEN只留轻量判断,比如状态码、非空检查、数值范围 - 性能影响:哪怕只是多一个
UPPER(:new.name),也会让WHEN失去谓词下推能力,某些 Oracle 版本可能跳过优化路径
UPDATE OF 列表 + WHEN 子句才是精准触发组合
单独靠 WHEN 拦不住无关列更新。比如你只想监控 salary 变化,但没写 UPDATE OF salary,那哪怕只改了 email 字段,触发器也会进 WHEN 判断——白白浪费一次上下文切换和解析开销。
- 必须同时声明:
BEFORE UPDATE OF salary ON employees WHEN (:new.salary != :old.salary) - 如果列名拼错或大小写不匹配(如表定义是
SALARY,却写update of salary),Oracle 不报错,但该触发器对这列更新完全不响应 -
UPDATE OF不支持表达式或函数,只接受逗号分隔的纯列名列表
WHEN 子句里别碰绑定变量和会话状态
WHEN 子句运行在 SQL 层,没有 PL/SQL 上下文,所以所有依赖会话状态的东西都会失效或报错。
- 禁止使用
USER、SYS_CONTEXT、SYSDATE等:Oracle 会提示ORA-04076: invalid NEW or OLD specification - 也不能引用触发器外定义的变量,比如包级变量
g_skip_log—— 它根本不可见 - 如果真需要按用户/时间/配置开关触发逻辑,得在触发器体里用
IF块做二次判断,WHEN只负责最粗粒度的行筛选
批量操作时 WHEN 子句不会帮你省掉开销
用 FORALL 或 SQL*Loader 批量更新时,WHEN 子句仍会对每行单独求值。它不提供“整批跳过”的能力,更不会提前终止批量过程。
- 如果你的
WHEN条件里有计算(比如:new.amount * :new.rate > 1000),10万行就要算10万次——这部分开销无法避免 - 想真正跳过大量数据?得靠 DML 语句本身加
WHERE,比如UPDATE employees SET ... WHERE status = 'ACTIVE',让数据在进触发器前就被筛掉 - Oracle 12c+ 支持在触发器里用
PRAGMA AUTONOMOUS_TRANSACTION,但这和WHEN无关,且容易引发事务隔离问题,慎用
真正卡住性能的往往不是 WHEN 表达式本身,而是它让你误以为“写了条件就等于跳过了执行”。实际中,只要触发器被挂上,每次 DML 都要走一遍解析、权限校验、伪记录构造——这些开销 WHEN 一概不管。


















