BEFORE INSERT触发器中应使用NEXTVAL安全获取序列新值并赋给:NEW.id,不可用CURRENTVAL(会报错或重复),需确保序列存在、权限充足、字段有效;高并发下序列值不保证连续,严格连续编号需应用层控制。

BEFORE INSERT 触发器里怎么安全调用 NEXTVAL
直接在触发器中写 SELECT seq_name.NEXTVAL INTO :NEW.id FROM DUAL 是可行的,但必须确保序列存在、权限足够,且触发器作用域明确。常见错误是触发器定义在无主键列的表上却试图赋值给不存在的字段,或序列名拼写错误导致 ORA-02289: sequence does not exist。
实操建议:
- 先确认序列已创建:
CREATE SEQUENCE my_seq START WITH 1 INCREMENT BY 1; - 触发器中必须用
:NEW.column_name赋值,不能用:OLD(BEFORE INSERT 时:OLD为空) - 避免在复合触发器或 FOR EACH ROW 触发器外调用
NEXTVAL,否则可能跳号或报错 - 若表有多个可选主键列(如 ID 可空),需加
IF :NEW.id IS NULL THEN ... END IF;防止覆盖手动插入值
为什么不能用 CURRENTVAL 替代 NEXTVAL
CURRENTVAL 返回的是当前会话最近一次 NEXTVAL 的值,不是“当前序列值”。在触发器里用它会导致:第一次插入失败(因为还没人调过 NEXTVAL),后续插入则重复使用同一个值,彻底破坏唯一性。
典型错误现象:ORA-08002: sequence MY_SEQ.CURRVAL is not yet defined in this session。
正确做法只有一条:始终用 NEXTVAL 获取新值,哪怕你只是想“延续上次值”,也得先调一次 NEXTVAL 再减一(不推荐,易并发冲突)。
INSERT ALL / 多行插入时触发器行为要注意什么
Oracle 的 BEFORE EACH ROW 触发器对 INSERT ALL 或批量 INSERT ... SELECT 仍会逐行触发,NEXTVAL 也会逐次递增——这是预期行为,不是 bug。但容易被忽略的是:如果语句中某行违反约束(如唯一键冲突),触发器已执行的 NEXTVAL 不会回滚,序列号就此跳过。
所以:
- 不要指望序列值连续,只依赖其唯一性和单调递增
- 高并发下避免用序列值做业务排序依据(比如“第 N 单”),应另加时间戳或版本字段
- 若需严格连续编号(如财务凭证号),序列 + 触发器不够用,得用应用层锁或专用编号服务
替代方案:12c+ 可以直接用 IDENTITY 列省掉触发器
Oracle 12c 引入了 GENERATED ALWAYS AS IDENTITY,语法更简洁、性能更好、还自动建隐式序列。例如:id NUMBER GENERATED ALWAYS AS IDENTITY。
优势明显:
- 无需手写触发器,减少维护出错概率
- 隐式序列权限由 Oracle 自动管理,不用额外授
SELECT权限 - 支持
START WITH、INCREMENT BY等参数,和显式序列一致 - 但注意:一旦设为
ALWAYS,就不能手动 INSERT 值;若需允许手动覆盖,得用BY DEFAULT ON NULL
老版本(11g 及以前)没这个选项,只能靠触发器 + 序列组合。而即便在新版本,如果要兼容旧逻辑、或需在赋值前做校验/转换(比如拼接前缀),触发器仍是不可替代的。


















