PL/SQL中必须用SELECT seq.NEXTVAL INTO var FROM dual,不能直接写SELECT...FROM dual;CURRVAL需先调NEXTVAL才能使用;DEFAULT含NEXTVAL时VALUES中不可重复写;批量插入需预取ID防跳号;CACHE导致异常重启后跳号属正常现象。
PL/SQL里直接用NEXTVAL会报错:ORA-00984
在pl/sql块中写select seq_name.nextval from dual;会直接报错,不是语法写错了,而是pl/sql不支持这种“纯sql查询语句”直接出现在执行体里——它要求必须把结果into一个变量。很多人卡在这一步,以为是序列没建好或权限问题,其实只是少了个接收容器。
- 必须用
SELECT seq_name.NEXTVAL INTO :variable FROM dual;形式 - 目标变量类型得是
NUMBER(或兼容的数值类型),不能是VARCHAR2 - 如果在存储过程中用,变量要先声明;在匿名块里也一样,不能跳过声明直接
INTO - 别在函数返回语句里直接写
RETURN seq.NEXTVAL,同样会报ORA-00984,得先查出来再return
NEXTVAL和CURRVAL在同一个会话里怎么配合用
NEXTVAL每次调用都递增并返回新值,CURRVAL只读取当前会话最后一次NEXTVAL产生的值。关键点是:CURRVAL不能在没调过NEXTVAL的会话里直接用,否则ORA-08002。
- 第一次必须用
NEXTVAL“激活”序列,之后才能用CURRVAL - 同一个会话中多次调
NEXTVAL,CURRVAL始终反映最新那个值 - 不同会话之间
CURRVAL互不影响,A会话的CURRVAL对B会话不可见 - 如果想在插入后马上拿到刚生成的ID,用
NEXTVAL插入,再用CURRVAL查——但更稳妥的是用RETURNING子句
在INSERT语句里嵌入NEXTVAL要注意空值和默认值冲突
常见做法是INSERT INTO t(id, name) VALUES (seq.NEXTVAL, 'abc');。看着没问题,但容易踩两个坑:一是字段有DEFAULT seq.NEXTVAL定义时,显式写NEXTVAL会导致重复触发;二是用INSERT ... SELECT批量插时,NEXTVAL每行都会算一次,但可能不符合预期(比如想统一用同一个值)。
- 如果列定义了
DEFAULT seq.NEXTVAL,就别在VALUES里再写seq.NEXTVAL,否则报ORA-02287:sequence number not allowed here - 批量插入多行时,
SELECT seq.NEXTVAL, col1 FROM src会让每行拿到不同序号——这是对的,但如果业务需要“同一批用同一个ID”,就得先SELECT seq.NEXTVAL INTO v_id FROM dual,再用v_id插入所有行 - 触发器里调
NEXTVAL要格外小心,避免主表插入、子表插入、日志表插入各自触发一次,导致ID跳号严重
序列缓存(CACHE)影响NEXTVAL行为但不改变正确性
建序列时加CACHE 20是为了性能,Oracle会一次性预分配20个值到内存。这不会让NEXTVAL变慢或出错,但有个现实影响:数据库异常重启后,缓存里没用完的那些值就丢了,下次NEXTVAL会从新缓存块开始,造成“跳号”。
- 只要不依赖连续ID(比如做关联或排序),跳号完全正常,也不算bug
- 如果真要绝对连续,建序列时用
NOCACHE,但高并发下性能明显下降,因为每次都要更新数据字典 -
ORDER选项只在RAC环境有意义,单实例下加不加ORDER对NEXTVAL结果没区别 - 查询
USER_SEQUENCES视图里的CACHE_SIZE和LAST_NUMBER,能帮你判断当前缓存状态,但LAST_NUMBER不是“最后用过的值”,而是“缓存块上限”
序列本身不记录谁用了NEXTVAL,也不锁行,所以并发安全。但正因为太轻量,所有“保证唯一+有序+不重用”的需求,都得靠应用层逻辑兜底,而不是指望序列自己扛住所有边界情况。










