【问题标题】:Need to reset the value of sequence in OracleOracle中需要重新设置sequence的值
【发布时间】:2023-04-06 14:22:01
【问题描述】:

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

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

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

alter table table_name auto_increment=1;

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

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

【问题讨论】:

  • 为什么需要序列密集?你如何使用它 ?也许您可以在查询中使用rank()(或dense_rank()
  • 为什么您认为必须重新使用已删除行中的 PK 值?这些数字完全没有意义。所以就继续 11 吧,不要在意。

标签: java oracle spring hibernate oracle10g


【解决方案1】:

在使用时不应重置该值的原因:

如果您有 20 条记录并删除 5-10 条记录会怎样?您中间有一个间隙,重新设置序列将无法解决。序列将never generate a gap free sequence of numbers,一个完美的1, 2 .. n

如果您调用.nextval 并且不使用该值,它已消失。您要删除并重新创建序列吗?如果您开始插入并取消它,Oracle rolls back 您所做的这些值将消失。如果您设置nocache,那么您的差距会更小,但会影响性能;这值得么?

您的缓存应设置为您希望在任何时候执行的插入次数across all sessions 以避免任何性能问题。序列旨在提供一种非常快速、可扩展的方式来创建没有任何锁等的代理键来重新生成正整数集。

归根结底,这应该无关紧要。如果您依赖完整的序列作为表的键,那么您的数据而不是序列有问题。


回答问题:

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

  1. 首先,找出表中的最大 id(序列)值是多少。
  2. 然后drop and re-create the sequence

找到最大值意味着您需要以再次影响性能为代价动态地重新创建序列。

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

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;
/

正如我所说,不建议这样做,您应该忽略任何差距。


更新 - 也就是更好的答案感谢Jeffrey Kemp

与文档的建议相反,正如 Jeffrey Kemp 在 cmets 中所建议的那样,有一种方法可以做到这一点删除并重新创建序列。

即,由:

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

这样做的好处是对象仍然存在,因此触发器、授权等仍然保持不变。在我看来,不利的一面是,如果另一个会话与您的会话同时增加这个负数,您可能会回退太远。

这是一个演示:

设置测试:

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>

【讨论】:

  • 不,您不需要需要删除并重新创建序列来重置它。您可以结合使用两个ALTER 语句和一个查询。
  • 我不知道,谢谢@jeffrey,我从来没有真正需要做这样的事情。虽然它仍然是 2 个语句,所以我猜的好处是对象仍然存在。
  • 是 - 包括授权和缓存设置等序列选项
  • 关于 Jeffrey Kemp 的回答,请注意使用 .currval - you need a call to .nextval before it can work。我不得不改用SELECT last_number FROM user_sequences WHERE sequence_name='SEQ_MY_TABLE';
【解决方案2】:

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

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

【讨论】:

  • 这对我不起作用,至少对于 oracle 版本 11g。我做了一个测试,我执行了这些语句:pastebin.com/th1Mkw8b。 “nextval”在执行“connect by level
  • 非常感谢您!幸运的是在浪费了我大约 3 个小时之后偶然发现了它。
【解决方案3】:

参考Oracle的ALTER SEQUENCE文档,

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

您需要传递主键列的当前最大值来生成下一个序列号。
如果您继续删除一些最新的记录,并且想要重用已经生成的序列值,您可能需要一次又一次地重新启动该序列。如果服务器在序列准备好服务之前收到对下一个值的请求,则删除序列可能会导致 SQLException。

您还可以在 Oracle 序列here 上找到一些有用的说明。

【讨论】:

  • 从技术上讲,您可以将序列更改为负递增,从中选择,然后重新更改为正递增。
  • @AdamMusch 是的,这是可能的,并且现有的序列对象不会丢失。
【解决方案4】:

这个技巧对我有用。 在我的序列 (USERSUTPL_USERUTPL_SQ) 中,最后一个数字是 53。 我表中的最后一个 id 是 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2021-09-16
    • 2022-11-24
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多