oracle 序列 current 与 nextval

本文详细解析了数据库Sequence的NEXTVAL与CURRVAL的使用方式及并发访问特性,通过实验验证了CURRVAL依赖于最近一次NEXTVAL的结果,并强调了两者在不同连接间的独立性。

今天在公司遇到怎么都获取不到 序列 current 的值。

代码如下:

final AccRunningDao dao = new AccRunningDao();
dao.setSessionFactory(getSessionFactory());
final long cId = 30001L;
assertEquals(1L, dao.createAccRunningByBuyCredit(cId));//通过ibatis服务进行nextval获取并利用其存为id
final String sql1 = "select AR_ID.CURRVAL FROM DUAL";
Object[] obj = this.dbUnitHelper.executeQuery(sql1);//这边通过jdbc去获取current
long arId = ((BigDecimal) obj[0]).longValue();

后来网上查看资料才了解到原因如下:


原文地址:http://www.cnblogs.com/sos-blue/p/4897515.html

以前写sql的时候总是担心current()得到的值并不会绝对等于我上一次nextval()取得的值;
因为可能其他线程并发访问nextval()。

先说结论吧:
当你拿到一个数据库连接,先nextval(),
然后无论其他再怎么操作这个sequence,你用current()取得的值一直都会与你刚才nextval()的取值相同

此时才明白了为什么当我想在数据库中直接查看某个sequence的currval时,报错,必须要先nextval(),才能执行currval()

我做了一个实验,用同一个用户开两个数据库连接A、B

第一步
用连接A调用nextval(),取得值为1
用连接A调用current(),取得值总为1;

第二步
用连接B调用nextval(),取得值为2
用连接B调用current(),取得值总为2;

第三步
然后在用连接A调用current(),取得值仍然为1;

以下内容转自百度贴吧http://tieba.baidu.com/f?kz=185551317,文中应该专指Informix数据库,部分可能不完全适用与其他数据。


使用 NEXTVAL
第一次访问一个序列,在引用 sequence.CURRVAL 之前必须先引用 sequence.NEXTVAL。第一次引用 NEXTVAL,返回序列的初始值。后面每次引用 NEXTVAL,用已定义的 step 增加序列值并返回序列新的增加以后的值。

在一个 SQL 语句中只能对给定的序列增加一次。即使在一个语句中多次指定 sequence.NEXTVAL,序列也只增加一次,所以每次 sequence.NEXTVAL 出现在同一 SQL 语句中返回相同的值。

除了在同一语句中多次出现这种情况以外,每个 sequence.NEXTVAL 表达式都会增加序列,无论后来是否提交或回滚当前事务。

如果在最终回滚的事务中指定 sequence.NEXTVAL,某些序列数可能被跳过。

使用 CURRVAL
任何对 CURRVAL 的引用返回指定序列的当前值,该值是最后一次对 NEXTVAL 的引用所返回的值。用 NEXTVAL 生成一个新值以后,可以继续使用 CURRVAL 访问这个值,不管另一个用户是否增加这个序列。

如 果 sequence.CURRVAL 和 sequence.NEXTVAL 都出现在一个 SQL 语句中,则序列只增加一次。在这种情况下,每个 sequence.CURRVAL 和 sequence.NEXTVAL 表达式都返回相同的值,不管在语句中 sequence.CURRVAL 和 sequence.NEXTVAL 的顺序。

序列的并发访问
序列总是在数据库中生成唯一值,即使当多个用户并发地引用同一序列时也没有可察觉的等待或锁定。当多个用户使用 NEXTVAL 来增长序列时,每个用户生成一个其他用户不可见的唯一值。

当多个用户并发地增加同一序列时,每个用户看到的值是有差异的。例如,一个用户可能从一个序列生成一组值,如 1、4、6 和 8,而另一个用户并发地从同一序列生成值 2、3、5 和 7。

### 重置 Oracle 数据库中的序列Oracle 数据库中,序列Sequence)是用于生成唯一数字值的对象,通常用于主键或唯一标识符。由于 Oracle 不直接支持重置序列的当前值,因此需要通过一些间接方法来实现。 一种常见的做法是利用 `ALTER SEQUENCE` 命令修改序列的增量值,然后调用 `NEXTVAL` 来跳转到目标值。例如,若希望将序列 `seq_test` 的下一个值重置为 100,可以先将增量设置为 100,再调用一次 `NEXTVAL`,之后将增量恢复为 1: ```sql ALTER SEQUENCE seq_test INCREMENT BY 100 NOCACHE; SELECT seq_test.NEXTVAL FROM dual; ALTER SEQUENCE seq_test INCREMENT BY 1; ``` 这种方法适用于临时调整序列值,但需注意在调整过程中使用 `NOCACHE` 选项,以避免缓存机制导致的不一致问题[^4]。 另一种更复杂的重置方法是创建一个存储过程,动态地将序列重置为表中某个字段的最大值加一。这种方式适用于主键序列值不一致的修复场景。例如,以下存储过程可以实现该功能: ```sql CREATE OR REPLACE PROCEDURE adjust_sequence( seq_name IN VARCHAR2, table_name IN VARCHAR2, column_name IN VARCHAR2, result OUT VARCHAR2 ) AS max_val NUMBER; current_val NUMBER; BEGIN EXECUTE IMMEDIATE 'SELECT MAX(' || column_name || ') FROM ' || table_name INTO max_val; EXECUTE IMMEDIATE 'SELECT ' || seq_name || '.nextval FROM dual' INTO current_val; WHILE current_val < max_val + 1 LOOP EXECUTE IMMEDIATE 'SELECT ' || seq_name || '.nextval FROM dual' INTO current_val; END LOOP; result := 'Sequence adjusted to ' || current_val; EXCEPTION WHEN OTHERS THEN result := 'Error adjusting sequence: ' || SQLERRM; ROLLBACK; END; ``` 此外,还有一种方法是通过删除并重新创建序列来重置其值。尽管这种方法简单直接,但存在一定的副作用,如依赖该序列的函数或存储过程将失效,需要重新编译[^3]。 如果希望将序列重置为特定值,例如 0,也可以使用类似的存储过程来实现。通过动态 SQL 执行 `ALTER SEQUENCE` 命令,并调整序列的最小值和增量值,从而达到重置的目的: ```sql CREATE OR REPLACE PROCEDURE reset_seq(p_seq_name IN VARCHAR2) IS BEGIN EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || p_seq_name || ' INCREMENT BY -100000 MINVALUE 0'; EXECUTE IMMEDIATE 'SELECT ' || p_seq_name || '.NEXTVAL FROM dual'; EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || p_seq_name || ' INCREMENT BY 1'; END; ``` 上述方法中,首先将增量设置为一个较大的负值,使当前值变为目标值,再调用一次 `NEXTVAL`,最后将增量恢复为 1[^2]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值