Oracle 11g及更早版本必须用SEQUENCE+TRIGGER模拟自增主键;建序列时START WITH需与表中当前最大ID对齐(如MAX(id)=999则设START WITH 1000),并显式指定INCREMENT BY 1、NOCACHE;触发器须含FOR EACH ROW和WHEN (:NEW.id IS NULL)条件,避免ORA-01400或覆盖业务传值。
Oracle 11g 及更早版本不支持 AUTO_INCREMENT,也未引入 IDENTITY 列(12c 起才有),所以必须用 SEQUENCE + TRIGGER 组合来模拟自增主键——这不是“ workaround”,而是生产环境的标准做法。
创建 Sequence 时必须校准 START WITH 值
如果表里已有数据,START WITH 设小了会报 ORA-00001: unique constraint violated;设大了则浪费号段。关键不是“从 1 开始”,而是和当前最大 ID 对齐:
- 先查当前最大值:
SELECT NVL(MAX(id), 0) FROM your_table - 若结果是
999,则建序列时写START WITH 1000 -
INCREMENT BY 1必须显式写出,避免误继承 session 级默认值 -
NOCACHE更稳妥:RAC 环境或异常重启时不会丢号;若选CACHE 20,得接受小概率跳号(比如插入 3 行后事务回滚,下次NEXTVAL直接跳到 24)
触发器里漏掉 FOR EACH ROW 或判断条件就白写了
常见错误是触发器编译通过但插入仍报 ORA-01400: cannot insert NULL,问题几乎都出在这两处:
- 没写
FOR EACH ROW→ 触发器只在语句级生效,不作用于每一行 - 没加
WHEN (:NEW.id IS NULL)或等价的IF :NEW.id IS NULL THEN ... END IF;→ 应用层偶尔传了 ID(比如导入旧数据),触发器却强行覆盖,可能破坏业务逻辑 - 别用
:OLD.id做判断,INSERT时:OLD为空,会报ORA-04082: NEW or OLD references not allowed in table level triggers
INSERT 显式指定 ID 不会导致 Sequence 跳号,但会“浪费”一个值
emp_id_seq.NEXTVAL 是独立递进的,它不感知你是否用了这个值。例如:
oracle知识库
oracle知识库下载
下载
- 当前
CURRVAL = 105 - 你执行
INSERT INTO emp (id, name) VALUES (100, 'Alice') - 下一次
NEXTVAL仍是106,不是101
这意味着:如果你依赖 ID 连续性做单据编号、审计追踪,Sequence 就不适合——它只保证唯一和递增,不保证连续。真要连续,得用应用层锁表+查最大值++1,但并发下性能差、易死锁。
并发插入时 CURRVAL 不可靠,别用它取刚插入的 ID
多个会话同时插入时,emp_id_seq.CURRVAL 返回的是“本会话最近一次调用 NEXTVAL 的值”,不是“刚插进去那条的 ID”。比如:
- 会话 A 执行
INSERT→ 触发器取走105 - 会话 B 紧接着也插入 → 取走
106 - 会话 A 此时查
CURRVAL,得到的是106(B 刚取的),不是自己的105
正确方式是用 RETURNING INTO(如 INSERT ... RETURNING id INTO :v_id),或在触发器里把 NEXTVAL 结果存入包变量再读——但后者仅限同会话内安全。