如何在回滚的情况下使用Oracle DB序列而不丢失下一个序列号

时间:2018-03-20 09:25:20

标签: database oracle sequence

问题

如何在回滚的情况下使用Oracle DB序列而不丢失下一个序列号?

收集的事实

1 - 在Oracle中,我们可以创建一个序列并使用两个主要调用(NEXTVAL)来获取下一个序列值,并使用(CURRVAL)来获取当前序列值。

2 - 当我们打电话时(NEXTVAL)将始终获得下一个号码,如果有回滚,我们将丢失它。换句话说,Oracle序列不关心是否存在回滚或提交;无论何时你打电话,它都会给你一个新号码。

到目前为止我找到的可能答案

1 - 我正在考虑创建一个包含一列类型(NUMBER)的简单表来为此目的服务。  只需选择值并使用它。如果操作成功,我将增加列值。否则,我将保留原样用于下一次应用程序调用。

2 - 我在这里找到的另一种方式(How do I reset a sequence in Oracle?)是使用(ALTER SEQUENCE)如下所示,如果我想退后一步。

也就是说,如果序列是101,我可以通过

将其设置为100
ALTER SEQUENCE serial INCREMENT BY -1;
SELECT serial.NEXTVAL FROM dual;
ALTER SEQUENCE serial INCREMENT BY 1;

结论

是否有任何建议的解决方案?他们有更好的方法吗?

2 个答案:

答案 0 :(得分:2)

从我的观点来看,你应该使用一个序列并且不要担心差距。

从你的角度来看,我会说改变序列更糟比拥有一张桌子。请注意,对该表的访问必须限制为单个用户,否则如果两个(或更多)用户同时访问它,您将获得重复值。

这是一个示例代码;看看,如果你愿意,可以使用/调整它。

SQL> create table broj (redni_br number not null);

Table created.

SQL>
SQL> create or replace function f_get_broj
  2     return number
  3  is
  4     pragma autonomous_transaction;
  5     l_redni_br   broj.redni_br%type;
  6  begin
  7         select b.redni_br + 1
  8           into l_redni_br
  9           from broj b
 10     for update of b.redni_br;
 11
 12     update broj b
 13        set b.redni_br = l_redni_br;
 14
 15     commit;
 16     return (l_redni_br);
 17  exception
 18     when no_data_found
 19     then
 20        lock table broj in exclusive mode;
 21
 22        insert into broj (redni_br)
 23             values (1);
 24
 25        commit;
 26        return (1);
 27  end f_get_broj;
 28  /

Function created.

SQL> select f_get_broj from dual;

F_GET_BROJ
----------
         1

SQL> select f_get_broj from dual;

F_GET_BROJ
----------
         2

SQL>

答案 1 :(得分:1)

您可以创建序列表。

CREATE TABLE SEQUENCE_TABLE
  (SEQUENCE_ID NUMBER, 
   SEQUENCE_NAME VARCHAR2(30 BYTE), 
   LAST_SEQ_NO NUMBER);

在PL / SQL块中,您可以使用下面的代码行

来获取序列
declare
    CURSOR c1 IS
       SELECT last_seq_no
         FROM sequence_table
        WHERE sequence_id = 21
          FOR UPDATE NOWAIT;
       v_last_seq_no NUMBER;
       v_new_seq_no NUMBER;
    resource_busy EXCEPTION;
    PRAGMA EXCEPTION_INIT(resource_busy, -54);
BEGIN
    LOOP
        BEGIN
           OPEN c1;
           FETCH c1 INTO v_last_seq_no;
           CLOSE c1;
           v_new_seq_no := v_last_seq_no+1;
           EXIT;
        EXCEPTION
            WHEN resource_busy THEN
                NULL;
                --or something you want to happen
        END;
   END LOOP;
   --after the this line, you can put an update to the sequence table and be sure to commit/rollback at the end of the pl/sql block;
END;
/


ROLLBACK;
--or
COMMIT;

尝试在两个oracle会话中运行上面的PL / SQL代码来理解。基本上,如果Oracle DB会话1将运行代码,则从光标查询的记录将被锁定。因此,如果其他会话将运行相同的代码,该会话将等待会话1的回滚/提交以完成代码运行。通过这个,两个会话不会有相同的sequence_no,如果由于某些原因发出回滚,你可以选择不更新序列。