Oracle中BEFORE INSERT触发器必须用SELECT seq.NEXTVAL INTO :NEW.id FROM dual赋值,不可用:NEW.id := seq.NEXTVAL,否则编译失败;须加WHEN (:NEW.id IS NULL)防止覆盖手动指定值,且序列需按表最大ID重置起始值。

BEFORE INSERT 触发器必须配合 SELECT ... INTO :NEW.id FROM dual,不能直接赋值,否则编译失败或报 ORA-00923。
为什么 :NEW.id := seq.NEXTVAL 会报错?
Oracle PL/SQL 不支持在触发器中用赋值语句直接给伪列 :NEW.xxx 赋序列值。常见错误写法::NEW.id := my_seq.NEXTVAL —— 这会直接导致编译失败。正确路径只有一条:SELECT my_seq.NEXTVAL INTO :NEW.id FROM dual。漏掉 FROM dual 会触发 ORA-00923: FROM keyword not found where expected;省略 INTO 或写成 := 则语法不合法。
WHEN (NEW.id IS NULL) 是防止覆盖的手动插入的关键
没加这个条件,哪怕你显式插入 INSERT INTO users(id, name) VALUES(100, 'Alice'),触发器也会强行把 id 改成序列下一个值,业务逻辑瞬间崩坏。实际使用中必须加上:
-
WHEN (:NEW.id IS NULL)—— 最稳妥,仅空值时介入 - 若允许
0表示“未指定”,可扩展为WHEN (:NEW.id IS NULL OR :NEW.id = 0) - 大小写敏感:如果建表时用了双引号定义列名(如
"Id"),这里也得写成:NEW."Id"
序列 CACHE 设置影响 ID 连续性与重启行为
生产环境设 CACHE 20 能显著提升批量插入吞吐量,但数据库异常宕机后,缓存中未用完的序列值会丢失,造成跳号(比如从 1000 直接跳到 1021)。这不是 bug,是 Oracle 的设计取舍。
- 开发/测试环境建议用
NOCACHE,保证绝对连续(代价是每次取值都要访问数据字典) - 绝对不要加
ORDER—— 在 RAC 环境下会强制全局串行生成,性能断崖式下跌 - 已存在数据的表,必须重置序列起始值,否则首次插入就报
ORA-00001(主键冲突)
已有数据的表怎么安全启用自增?
这是最容易被跳过的一步。假设表里最大 id 是 999,而新序列从 1 开始,第一次插入必失败。必须先查、再调、再验:
- 查当前最大值:
SELECT NVL(MAX(id), 0) FROM my_table - Oracle 12c+ 直接重置:
ALTER SEQUENCE my_seq RESTART WITH 1000 - 旧版本需三步:
ALTER SEQUENCE my_seq INCREMENT BY 1000→SELECT my_seq.NEXTVAL FROM dual→ALTER SEQUENCE my_seq INCREMENT BY 1 - 触发器一旦启用,对所有
INSERT生效,包括应用层传NULL——这点和 MySQL 的DEFAULT NULL语义不同,得提前对齐业务预期


















