Oracle通过序列+触发器实现自增ID,需在BEFORE INSERT触发器中添加WHEN (NEW.id IS NULL)条件以避免覆盖手动指定值,且必须用SELECT ... INTO :NEW.id FROM dual赋值,不可直接赋值或省略FROM dual。

Oracle 本身不支持 AUTO_INCREMENT,必须用 SEQUENCE + 行级触发器组合实现;直接写 BEFORE INSERT 触发器但漏掉 WHEN 条件或误用 CURRVAL 是最常见翻车点。
CREATE TRIGGER 里必须加 WHEN (NEW.id IS NULL)
否则每次插入都会覆盖你手动指定的 id 值,导致业务逻辑出错。比如你执行 INSERT INTO users(id, name) VALUES(100, 'Alice'),没加这个条件的话,触发器仍会强行把 id 改成序列下一个值。
- 只在
id显式为空时才赋值:WHEN (NEW.id IS NULL) - 表名、列名、序列名全部区分大小写(若建表时用了双引号)
- 触发器名建议带前缀,如
trg_users_id_autoinc,避免和其它表冲突
SELECT ... INTO :NEW.id FROM dual 是标准写法,但 PL/SQL 块里不能省略 FROM dual
很多人从 SQL 语句迁移到触发器时误以为“seq.NEXTVAL 能直接赋值”,结果写出 :NEW.id := seq.NEXTVAL —— 这在 Oracle 中语法错误。PL/SQL 不支持这种赋值方式,必须走 SELECT ... INTO。
- 正确:
SELECT my_seq.NEXTVAL INTO :NEW.id FROM dual; - 错误:
:NEW.id := my_seq.NEXTVAL;(编译失败) - 错误:
SELECT my_seq.NEXTVAL INTO :NEW.id;(缺FROM dual,报 ORA-00923)
序列 CACHE 设置影响高并发下的 ID 连续性与性能
如果设了 CACHE 20,Oracle 会一次性预分配 20 个值到内存;数据库异常重启后,未用完的缓存值就丢失,造成“跳号”。这对主键唯一性无影响,但如果你依赖连续 ID 做业务判断(比如分页推算),就会出问题。
- 开发/测试环境可加
NOCACHE,保证绝对连续(性能略低) - 生产环境推荐
CACHE 20或更高,提升大批量插入吞吐量 - 别用
ORDER,它强制序列按请求顺序生成,会严重拖慢 RAC 环境下的性能
已存在数据的表,必须重置序列起始值
如果表里已有 1000 条记录,最大 id 是 999,而你新建的序列从 1 开始,下一次插入就会报主键冲突(ORA-00001)。必须先查出当前最大值,再重置序列。
- 查最大值:
SELECT NVL(MAX(id), 0) FROM my_table; - 重置序列:
ALTER SEQUENCE my_seq RESTART WITH 1000;(Oracle 12c+) - 旧版本用:
ALTER SEQUENCE my_seq INCREMENT BY 1000; SELECT my_seq.NEXTVAL FROM dual; ALTER SEQUENCE my_seq INCREMENT BY 1;
真正容易被忽略的是:触发器一旦创建,它就对所有插入生效,包括应用层显式传入 NULL 的情况——这和 MySQL 的 DEFAULT NULL 行为不同。务必确认你的 ORM 或 JDBC 层是否可能悄悄传 NULL,否则会意外触发自增逻辑。


















