在before insert/update触发器中,用:new伪记录(如:new.phone)访问待插入/更新值,需声明for each row,校验失败时用raise_application_error中断;:new仅行级触发器可用,语句级会报ora-04082。

直接在 BEFORE INSERT 或 BEFORE UPDATE 触发器里写校验逻辑,是最常用、最可靠的方式——它能真正阻止非法数据入库,而不是等事后补救。
触发器里怎么访问待插入/更新的值?
必须用 :new 伪记录,比如 :new.phone、:new.email。但前提是触发器声明了 FOR EACH ROW,否则 :new 不可用,会报 ORA-04082: NEW or OLD references not allowed in table level triggers。
- 不加
FOR EACH ROW→ 触发器是“语句级”,:new和:old都不可用 - 加了
FOR EACH ROW→ 触发器是“行级”,才能逐行校验 - 校验代码必须放在
BEGIN ... END;块内,不能只写裸IF - 中断执行必须用
raise_application_error(-20001, '手机号格式错误'),错误码只能是 -20000 到 -20999
正则和内置函数够用吗?别急着写自定义逻辑
Oracle 的 REGEXP_LIKE、SUBSTR、INSTR、LENGTH 组合起来,能覆盖绝大多数常见校验场景,比手写 PL/SQL 循环或查表更轻量、更安全。
- 手机号:
REGEXP_LIKE(:new.phone, '^1[3-9]\d{9}$') - 邮箱白名单:
REGEXP_LIKE(:new.email, '@(?i)(company\.com|partner\.org)$') - 密码强度:
LENGTH(:new.password) >= 8 AND REGEXP_LIKE(:new.password, '[a-z]') AND REGEXP_LIKE(:new.password, '[A-Z]') AND REGEXP_LIKE(:new.password, '[0-9]') - 避免用
UTL_HTTP或UTL_FILE——需要额外授权,且超时会直接导致 DML 失败
为什么不能在触发器里查其他大表?
因为每一条 INSERT 或 UPDATE 都会触发一次查询,高并发下极易成为瓶颈。比如在触发器里写 SELECT COUNT(*) FROM audit_log WHERE user_id = :new.user_id,一秒上千次插入时,这个查询就会卡住整个业务链路。
- 查配置类小表(如状态码表、白名单表)可以接受,但要确保有索引
- 复杂规则别硬塞进触发器,优先外置到配置表 + 简单 SQL 查询,比如用
SELECT 1 FROM valid_domains WHERE domain = LOWER(SUBSTR(:new.email, INSTR(:new.email,'@')+1)) - 真要调 Java?先确认 PL/SQL 确实表达不了——比如调第三方加密 SDK。否则纯属增加故障面
最容易被忽略的是递归和隐式事务:触发器里再改同一张表,可能引发 ORA-04091: table is mutating;而哪怕只是多一句 INSERT INTO log_table,也会让所有 DML 变慢。校验越靠近数据入口,越要克制“顺便做点别的”冲动。











