Oracle
 sql >> Base de données >  >> RDS >> Oracle

Comment implémenter des séquences multidimensionnelles

La seule façon de le faire est d'utiliser une table de contrôle de code ...

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
/   

... qui se maintient comme ça ...

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

L'important est le SELECT ... FOR UPDATE. Le verrouillage pessimiste garantit l'unicité dans un environnement multi-utilisateurs. Le PRAGMA assure que le maintien de code_control ne pollue pas la transaction plus large. Cela nous permet d'appeler la fonction dans un déclencheur sans blocage.

Voici un tableau avec une clé comme la vôtre :

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

Il n'y a rien dans mes manches avant de remplir 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> 

L'objection évidente à cette implémentation est donc l'évolutivité. Les transactions d'insertion sont sérialisées sur le code_control table. C'est absolument vrai. Cependant, le verrou est maintenu pendant le temps le plus court possible, donc cela ne devrait pas être un problème même si le t42 table est remplie plusieurs fois par seconde.

Cependant, si la table est soumise à un nombre massif d'insertions simultanées, le verrouillage peut devenir un problème. Il est essentiel que la table dispose de suffisamment d'emplacements de transaction intéressée (INITRANS, MAXTRANS) pour faire face aux demandes simultanées. Mais les systèmes très occupés peuvent nécessiter une implémentation plus intelligente (générant peut-être les ID par lots); sinon, abandonnez la clé composée au profit d'une séquence (car les séquences évoluent dans les environnements multi-utilisateurs).