Salman
Salman

Reputation: 1262

How to use Oracle DB sequences without losing the next sequence number in case of roll-back

Question

How to use Oracle DB sequences without losing the next sequence number in case of roll-back?

Facts collected

1 - In Oracle, we can create a sequence and use two main calls (NEXTVAL) to get the next sequence value and (CURRVAL) to get the current sequence value.

2 - When we call (NEXTVAL) will always get the next number and we will lose it if there is a rollback. In other words, Oracle sequence does not care if there is a roll-back or commit; whenever you are calling it, it will give a new number.

Possible answers I found so far

1 - I was thinking to create a simple table with one column of type (NUMBER) to service this purpose. Simply pick the value and use it. If operation succeeded I will increment column value. Otherwise, I will keep it as it is for the next application call.

2 - Another way I found here (How do I reset a sequence in Oracle?) is to use (ALTER SEQUENCE) like the following if I want to go one step back.

That is, if the sequence is at 101, I can set it to 100 via

ALTER SEQUENCE serial INCREMENT BY -1;
SELECT serial.NEXTVAL FROM dual;
ALTER SEQUENCE serial INCREMENT BY 1;

Conclusion

Are any of the suggested solutions is good? Is their any better approach?

Upvotes: 0

Views: 1779

Answers (2)

eifla001
eifla001

Reputation: 1157

You can create a sequence table.

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

And in your PL/SQL block, you can get the sequence using below lines of code,

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;

Try to run the PL/SQL code above in two oracle sessions to understand. Basically, if Oracle DB session 1 will run the code, the record queried from the cursor will be lock. So, if other session will run the same code, that session will wait for session 1's rollback/commit to finish running the code. Through this, two sessions won't have the same sequence_no and you have a choice not to update the sequence if you issue a rollback for some reasons.

Upvotes: 1

Littlefoot
Littlefoot

Reputation: 142788

From my point of view, you should use a sequence and stop worrying about gaps.

From your point of view, I'd say that altering the sequence is worse than having a table. Note that access to that table must be restricted to a single user, otherwise you'll get duplicate values if two (or more) users access it simultaneously.

Here's a sample code; have a look, use/adjust it if you want.

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>

Upvotes: 2

Related Questions