2

For example, here is a yearly sequence. The no increments with year:

| no | year |
+----+------+
|  1 | 2016 |
|  2 | 2016 |
|  3 | 2016 |
|  1 | 2017 |
|  2 | 2017 |
|  4 | 2016 |

For now I have created sequence for each year
but the problem is Oracle will not automatically create new sequence in next year.

Another problem is if I want to use a 3D sequence, incrementing within year and type:

| no | year | type |
+----+------+------+
|  1 | 2016 |    a |
|  2 | 2016 |    a |
|  1 | 2016 |    b |
|  1 | 2017 |    b |
|  2 | 2017 |    b |
|  1 | 2017 |    c |

This will be too many sequences in database

I do not recommend max(no) because of parallel access issue. I tried to lock table before getting max(no) in a trigger but it resulted in deadlock.

APC
  • 144,005
  • 19
  • 170
  • 281
Charles Chou
  • 179
  • 1
  • 15
  • You can just use normal sequence and then use row_number with partition by option while selecting the data to simulate the effect. – Gurwinder Singh Apr 28 '17 at 03:27
  • Looking to your sample, it's not a sequence, it's composit Primary key, and think it must be calculated in other way. What is the idea, to make unique set of columns, or have an incremental sequence for ordering? – Seyran Apr 28 '17 at 03:44
  • @Gurv Using runtime calculated "Id" is not stable, If I delete the medium item the last item Id will change. – Charles Chou Apr 28 '17 at 14:34
  • @Seyran In fact have the "ID" column as primary key. the composit uniqe "no-year-type" just show to the users. But I still wish this can generate by the database. – Charles Chou Apr 28 '17 at 14:37

1 Answers1

5

The only way to do this is with a code control table ...

create table code_control
    (year number(4,0) not null
     , type varchar2(1) not null
     , last_number number(38,0) default 1 not null
     , primary key (year,type)
    )
organization index
/   

... which is maintained like this ...

create or replace function get_next_number
    (p_year in number, p_type in varchar2)
    return number
is
    pragma autonomous_transaction;
    cursor cur_cc is
        select last_number + 1
        from code_control cc
        where cc.year= p_year
        and cc.type = p_type
        for update of last_number;
    next_number number;
begin
    open cur_cc;
    fetch cur_cc into next_number;
    if cur_cc%found then
        update code_control
        set last_number = next_number
        where current of cur_cc;
    else
        insert into code_control (year,type)
        values (p_year, p_type)
        returning last_number into next_number;
    end if;    
    commit;
    return next_number;
end;
/

The important thing is the SELECT ... FOR UPDATE. Pessimistic locking guarantees uniqueness in a multi-user environment. The PRAGMA ensures that maintaining code_control doesn't pollute the broader transaction. It allows us to call the function in a trigger without deadlocks.

Here is a table with a key like yours:

create table t42
     (year number(4,0) not null
     , type varchar2(1) not null
     , id number(38,0) 
     , primary key (year,type, id)
)
/
create or replace trigger t42_trg
    before insert on t42 for each row
begin
    :new.id := get_next_number(:new.year, :new.type);
end;
/

There's nothing up my sleeves before I populate t42:

SQL> select * from code_control;

no rows selected

SQL> select * from t42;

no rows selected

SQL> insert into t42 (year, type) values (2016, 'A');

1 row created.

SQL> insert into t42 (year, type) values (2016, 'A');

1 row created.

SQL> insert into t42 (year, type) values (2016, 'A');

1 row created.

SQL> insert into t42 (year, type) values (2016, 'B');

1 row created.

SQL> insert into t42 (year, type) values (2016, 'A');

1 row created.

SQL> insert into t42 (year, type) values (2017, 'A');

1 row created.

SQL> select * from t42;

      YEAR T         ID
---------- - ----------
      2016 A          1
      2016 A          2
      2016 A          3
      2016 A          4
      2016 B          1
      2017 A          1

6 rows selected.

SQL> select * from code_control;

      YEAR T LAST_NUMBER
---------- - -----------
      2016 A           4
      2016 B           1
      2017 A           1

SQL> 

So the obvious objection to this implementation is scalability. Inserting transactions are serialized on the code_control table. That's absolutely true. However the lock is held for the shortest possible time, so this should not be an issue even if the t42 table is populated many times a second.

However, if the table is subjected to massive numbers of concurrent inserts the locking may become an issue. It is crucial the table has sufficient Interested Transaction slots (INITRANS, MAXTRANS) to cope with concurrent demands. But very busy systems may need a smarter implementation (perhaps generating the IDs in batches); otherwise abandon the compound key in favour of a sequence (because sequences do scale in multi-user environments).

APC
  • 144,005
  • 19
  • 170
  • 281