必须用 before update 触发器,因其在约束检查前执行,可真正中止操作;after update 时数据已写入,raise_application_error 拦不住变更。

必须用 BEFORE UPDATE 触发器,且校验逻辑要写死在触发体里——任何查表、调函数、隐式时区转换都可能引发 ORA-00604 或死锁。
为什么只能用 BEFORE UPDATE,不能用 AFTER
AFTER UPDATE 触发时数据已写入,RAISE_APPLICATION_ERROR 拦不住变更;而 BEFORE UPDATE 在约束检查前执行,能真正中止操作。如果误写成 AFTER,表面上报错,但行数据其实已被修改,审计和一致性全失效。
工作日判断必须显式指定 NLS 语言
Oracle 的 TO_CHAR(SYSDATE, 'D') 返回值依赖会话的 NLS_DATE_LANGUAGE,不指定就可能把周一当周日。正确写法是:
TO_CHAR(SYSDATE, 'D', 'NLS_DATE_LANGUAGE=AMERICAN') NOT IN ('1','7')
其中 '1' 是周日,'7' 是周六。别用 'DY' 格式(如 'SAT'),它受 NLS_TERRITORY 影响更大,更不稳定。
工作时间边界必须用字符串比较而非数字
用 TO_CHAR(SYSDATE, 'HH24') 得到的是字符串,直接写 BETWEEN '09' AND '17' 才能天然覆盖 09:00:00 到 17:59:59 全区间。若改用数字比较,比如 EXTRACT(HOUR FROM SYSDATE) BETWEEN 9 AND 17,看似等价,但 Oracle 可能因隐式类型转换引入不可控开销,且某些补丁版本下会跳过索引优化路径。
- 错误:
TO_CHAR(SYSDATE, 'HH24') BETWEEN '09' AND '18'→ 放过 18:00:00 - 错误:
HOUR(SYSDATE) >= 9 AND HOUR(SYSDATE) → <code>HOUR()不是 Oracle 函数,会报错 - 务必加 DBA 白名单:
IF USER IN ('SYS', 'SYSTEM', 'DBA_MAINT') THEN RETURN; END IF;,否则维护窗口被锁死
触发器里禁止做任何 SQL 查询或函数调用
哪怕只查一次 DUAL,或调用自己写的 is_workday(),只要函数体里含 SELECT 或事务控制,就会触发递归 SQL。Oracle 在 BEFORE UPDATE 中严格限制纯计算表达式。安全范围仅限:
TO_CHAR(SYSDATE, ...)SYS_CONTEXT('USERENV', 'SESSION_USER')- 硬编码字符串、数字、布尔逻辑
连 SYSDATE - INTERVAL '8' HOUR 都建议避免——不是语法错,而是部分 Oracle 版本会对 interval 计算做隐式绑定,增加 parse 时间。当前时间就是 SYSDATE,别绕。











