需要重置Oracle中序列的值

我正在使用Spring和Hibernate来开发Java应用程序。 假设我有一张桌子。 当我从这个表中删除一些logging时,有时我需要重置主键字段的值。

假设我有一个表中有10条logging,我删除了最后5条logging。 现在,当我插入新logging时,主键字段的值应该从6开始,但是从11开始。

如果我需要在MySql中以6maximum +1 )启动主键值,那么我只需要执行下面的SQL语句。

 alter table table_name auto_increment=1; 

这会自动将auto_increment的值重置为该字段的maximum + 1值(可能在概念上不正确,但是可行)。

在Oracle(10g)中,我使用主键sequence 。 当Oracle从数据库中删除一些logging时,是否有办法将sequence的值重置为maximum + 1值?

如果您正在使用该值,则不应重置该值的原因:

如果你有20条logging,删除5-10条logging会怎么样? 你在中间有一个缺口,重新设定序列不会解决。 序列永远不会产生无间隙的数字序列 ,是一个完美的1,2 … n

如果你调用.nextval ,不要使用它的值。 你要丢弃并重新创build序列吗? 如果你开始插入并取消它,Oracle 回滚你所做的那些值,那么这些值就不存在 。 如果你设置nocache那么你将有较less的差距,但成本打击性能; 这值得么?

应该将caching设置为您希望在所有会话中一次执行的插入次数,以避免任何性能问题。 序列的目的是提供一个非常快速,可扩展的方式创build一个代理键没有任何锁等重新生成一组正整数。

在一天结束的时候,丝毫不应该有任何问题。 如果你依靠一个不间断的序列作为你的表的关键,那么你的数据有问题,而不是序列。


回答这个问题:

要真正回答你的问题,你需要:

  1. 首先,找出你的表中最大的id(sequence)值是多less。
  2. 然后删除并重新创build序列 。

find最大值意味着你需要重新创build序列dynamic的代价是另一个命中performance。

如果在发生这种情况时尝试向表格中插入某些内容,将会失败,并可能使使用该顺序的任何触发器或其他对象失效:

 declare l_max_value number; begin select max(id) into l_max_value from my_table; execute immediate 'drop sequence my_sequence_name'; -- nocache is not recommended if you are inserting more than -- one row at a time, or inserting with any speed at all. execute immediate 'create sequence my_sequence_name start with ' || l_max_value || ' increment by 1 nomaxvalue nocycle nocache'; end; / 

正如我所说这不build议,你应该忽略任何差距。


更新 – 又一个更好的答案感谢Jeffrey Kemp :

与文件build议相反,正如杰弗里·肯普(Jeffrey Kemp)在评论中提出的那样,在丢弃和重新创build序列的情况下 ,可以这样做。

即,通过:

  1. 计算表中最大的id和序列的当前值之间的差异。
  2. 改变序列以这个负数递增
  3. 改变顺序再次增加1。

这样做的好处是,对象仍然存在,触发器,赠款等仍然保持。 我认为不利的一面是,如果另一个会议与此同时以这个负数增加,则可能会回到过去。

这是一个示范:

设置testing:

 SQL> create sequence test_seq 2 start with 1 3 increment by 1 4 nomaxvalue 5 nocycle 6 nocache; Sequence created. SQL> SQL> create table tmp_test ( id number(16) ); Table created. SQL> SQL> declare 2 l_nextval number; 3 begin 4 5 for i in 1 .. 20 loop 6 insert into tmp_test values ( test_seq.nextval ); 7 end loop; 8 9 end; 10 / PL/SQL procedure successfully completed. SQL> SQL> select test_seq.currval from dual; CURRVAL ---------- 20 SQL> SQL> delete from tmp_test where id > 15; 5 rows deleted. SQL> commit; Commit complete. 

恢复序列

 SQL> SQL> declare 2 3 l_max_id number; 4 l_max_seq number; 5 6 begin 7 8 -- Get the maximum ID 9 select max(id) into l_max_id 10 from tmp_test; 11 12 -- Get the current sequence value; 13 select test_seq.currval into l_max_seq 14 from dual; 15 16 -- Alter the sequence to increment by the difference ( -5 in this case ) . 17 execute immediate 'alter sequence test_seq 18 increment by ' || ( l_max_id - l_max_seq ); 19 20 -- 'increment' by -5 21 select test_seq.nextval into l_max_seq 22 from dual; 23 24 -- Change the sequence back to normal 25 execute immediate 'alter sequence test_seq 26 increment by 1'; 27 28 end; 29 / PL/SQL procedure successfully completed. SQL> SQL> select test_seq.currval from dual; CURRVAL ---------- 15 SQL> 

以下也有效(将您的序列值增加100):

 select my_sequence.nextval from dual connect by level <= 100; 

参考Oracle的ALTER SEQUENCE文档,

  • 要以不同的编号重新启动序列,您必须删除并重新创build它。

您需要传递主键列的当前最大值以生成下一个序列号。
如果您不断删除一些最新的logging,并希望重新使用已经生成的序列值,则可能需要一次又一次地重新启动该序列。 如果服务器在序列准备好服务之前接收到下一个值的请求,则删除序列可能会导致SQLException。

您也可以在这里find有关Oracle序列的一些有用的注意事项。

这个把戏适合我。 在我的序列(USERSUTPL_USERUTPL_SQ)中,最后一个数字是53.我表格中的最后一个数字是83

 SELECT MAX(ID) FROM USERSUTPL_USERUTPL 

那么83 – 53 = 31.所以:

 ALTER SEQUENCE USERSUTPL_USERUTPL_SQ INCREMENT BY +31; SELECT USERSUTPL_USERUTPL_SQ.NEXTVAL FROM dual; ALTER SEQUENCE USERSUTPL_USERUTPL_SQ INCREMENT BY 1; 

它的变化是最后一个数字。 :d