Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie man mehrdimensionale Sequenzen implementiert

Das geht nur mit einer Code-Control-Tabelle ...

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
/   

... die so gepflegt wird ...

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;
/

Wichtig ist das SELECT ... FOR UPDATE. Pessimistisches Sperren garantiert Eindeutigkeit in einer Umgebung mit mehreren Benutzern. Das PRAGMA stellt sicher, dass code_control beibehalten wird verschmutzt nicht die umfassendere Transaktion. Es erlaubt uns, die Funktion in einem Trigger ohne Deadlocks aufzurufen.

Hier ist eine Tabelle mit einem Schlüssel wie Ihrem:

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;
/

Ich habe nichts im Ärmel, bevor ich t42 fülle :

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> 

Der offensichtliche Einwand gegen diese Implementierung ist also die Skalierbarkeit. Das Einfügen von Transaktionen wird auf dem code_control serialisiert Tisch. Das ist absolut wahr. Die Sperre wird jedoch so kurz wie möglich gehalten, sodass dies kein Problem sein sollte, selbst wenn t42 Tabelle wird viele Male pro Sekunde gefüllt.

Wenn die Tabelle jedoch einer großen Anzahl gleichzeitiger Einfügungen ausgesetzt ist, kann das Sperren zu einem Problem werden. Es ist entscheidend, dass die Tabelle über genügend Slots für interessierte Transaktionen (INITRANS, MAXTRANS) verfügt, um mit gleichzeitigen Anforderungen fertig zu werden. Aber sehr ausgelastete Systeme benötigen möglicherweise eine intelligentere Implementierung (vielleicht das Generieren der IDs in Stapeln); Andernfalls verzichten Sie auf den zusammengesetzten Schlüssel zugunsten einer Sequenz (da Sequenzen in Umgebungen mit mehreren Benutzern skalieren).