如何安全地重置Oracle序列?

时间:2019-05-23 08:03:08

标签: oracle sequence

我想每天将我的Oracle Sequence重置为0,如下所示:

create or replace procedure reset_seq(p_seq_name in varchar2) is
  l_val number;
begin
  execute immediate 'select ' || p_seq_name || '.nextval from dual'
    INTO l_val;   --1

  execute immediate 'alter sequence ' || p_seq_name || ' increment by -' ||
                    l_val || ' minvalue 0'; --2
  execute immediate 'select ' || p_seq_name || '.nextval from dual'
    INTO l_val;  --3

  execute immediate 'alter sequence ' || p_seq_name ||
                    ' increment by 1 minvalue 0';  --4

end;

但是运行约2年后,突然出现错误,增量是一个负整数,如-16,起始值也为-16。所以任何人都可以帮助我解释这个问题。

我认为是那样。

  1. 过程执行到步骤3,之后当前增量为-16,nextval为0。
  2. 其他客户端请求序列,但当前增量为-16,因此下一个值为-16
  3. 该过程运行至步骤4时发生了异常。

但是我不确定,任何人都可以向我解释。谢谢。

1 个答案:

答案 0 :(得分:0)

问题在于您提交的过程和您的外部程序都可能在步骤2和步骤4之间都调用nextval。可以按任何顺序调用;并可能使外部程序可以进行多次调用。

您可以尝试通过更改过程和外部调用来减轻这种情况。

如果第3步出错,此过程将终止,并且永远不会到达第4步,因此以后对nextval的所有调用将继续出错。因此,您可以捕获并忽略ORA-08004错误,因为您将仅获得以下信息:如果在步骤2和3之间进行了外部调用,则该调用将序列加回到零,因此3在这种情况下实际上是多余的:

create or replace procedure reset_seq(p_seq_name in varchar2) is
  l_val number;
  e_8004 exception;
  pragma exception_init(e_8004, -8004);
begin
  execute immediate 'select ' || p_seq_name || '.nextval from dual'
    INTO l_val;   --1

  execute immediate 'alter sequence ' || p_seq_name || ' increment by -' ||
                    l_val || ' minvalue 0'; --2

  begin
    execute immediate 'select ' || p_seq_name || '.nextval from dual'
      INTO l_val;  --3
  exception
    when e_8004 then
      -- nextval has already been called by someone else
      null;
  end;

  execute immediate 'alter sequence ' || p_seq_name ||
                    ' increment by 1 minvalue 0';  --4

end;

现在,如果外部程序在第2步和第3步之间调用nextval,那么您将在第3步得到错误,但忽略它,第4步仍然会发生。

如果外部程序在第2步和第3步之间进行调用,则其nextval值将为零;如果在第3步和第4步之间进行了调用,或者在第2步和第4步之间在窗口中进行了多次调用,则将获得ORA-08004。因此,假设您不希望零为有效结果(这看起来很合理)您通常不会得到),您可以重复拨打电话,直到得到非零答案且没有错误为止。用伪代码类似:

loop
  val = seq.nextval;
  if error == -8004 then continue;
  if val == 0 then continue;
  break;
end loop

您可以考虑将该逻辑放入PL / SQL函数中,并让您的程序调用该函数而不是直接访问该序列,这既可以隐藏该复杂性,又可以避免在可能影响多个程序的情况下重复执行该操作