How to implement multidimensional sequences

 ̄綄美尐妖づ 提交于 2019-12-19 10:28:06

问题


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.


回答1:


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).



来源:https://stackoverflow.com/questions/43671376/how-to-implement-multidimensional-sequences

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!