必须用BEFORE UPDATE触发器拦截非工作时间更新,AFTER触发器因数据已提交无法回滚;需指定FOR EACH ROW以访问:OLD/:NEW;避免查表或调用含查询函数以防ORA-04091;时间判断用TO_CHAR(SYSDATE,'HH24')字符串比较,周几用TO_CHAR(SYSDATE,'D','NLS_DATE_LANGUAGE=AMERICAN')确保一致性。

BEFORE UPDATE触发器必须用,AFTER的没用
想拦住非工作时间的UPDATE,只能用BEFORE UPDATE触发器。AFTER类型在数据已经写入表之后才执行,此时再RAISE_APPLICATION_ERROR只会报ORA-00604,无法回滚修改。你看到的数据污染已经发生。
常见错误现象:开发写了AFTER触发器+raise_application_error,测试时看似“报错”,但查表发现数据已被更新——因为AFTER触发点太晚。
- 必须指定
FOR EACH ROW,否则无法访问:OLD和:NEW伪记录 - 不能在触发器里查表、调用含查询的函数,否则引发递归SQL,报
ORA-00604+ORA-04091 - 时间判断直接用
TO_CHAR(SYSDATE, 'HH24')转字符串比较,别TO_NUMBER()——隐式转换在某些NLS设置下会失败
工作时间判断逻辑要绕开NLS陷阱
Oracle的TO_CHAR(SYSDATE, 'D')返回周几,但值取决于NLS_TERRITORY:美国环境周日=1,中国环境可能周一=1。生产库不统一就容易锁错日子。
稳妥做法是显式指定语言:TO_CHAR(SYSDATE, 'D', 'NLS_DATE_LANGUAGE=AMERICAN'),确保周一=2、周五=6、周末=1/7。
- 小时段建议用字符串区间:
'08' ,覆盖8:00–17:59 - 避免用
BETWEEN '8' AND '17'——单字符'8'和双字符'08'比较结果不可靠 - 如果业务允许周末紧急更新,可在条件里加白名单:
USER NOT IN ('APP_ADMIN', 'ETL_USER')
触发器里别碰事务、别写日志表
触发器运行在目标表DML事务上下文中,它自己不是独立事务。一旦在里面做INSERT INTO log_table,就会和主事务绑定,极易触发ORA-00604 + ORA-01031(权限不足)或ORA-04091(表正在变化)。
- 真要留痕,改用
DBMS_SYSTEM.KSDWRT写alert日志,或外部程序轮询V$SESSION捕获异常登录 - 禁止调用任何含
SELECT的自定义函数,哪怕只是查个配置表 - 不要用
SYSDATE以外的时间函数,CURRENT_TIMESTAMP在部分版本触发器中行为不稳定
测试时连不上?先确认触发器状态和用户权限
触发器创建后默认启用,但常被误禁用。连不上数据库时,先用DBA账号查:SELECT trigger_name, status FROM dba_triggers WHERE triggering_event LIKE '%UPDATE%' AND table_name = 'YOUR_TABLE';
更隐蔽的问题是:触发器由SYS创建,但执行时以登录用户权限运行。如果该用户没被授予EXECUTE权限给触发器里用到的任何PL/SQL包,也会静默失败。
- 临时禁用触发器命令:
ALTER TRIGGER your_trigger_name DISABLE; - 对运维账号(如
SYS、SYSTEM)务必放行,否则半夜出问题自己也登不进去了 - 时间判断逻辑建议拆成独立函数调试,但上线时仍要内联——函数调用本身可能引入不可控开销


















