Oracle
 sql >> Database >  >> RDS >> Oracle

Come implementare sequenze multidimensionali

L'unico modo per farlo è con una tabella di controllo del codice...

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
/   

... che si mantiene così ...

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'importante è SELEZIONA... PER AGGIORNARE. Il blocco pessimistico garantisce l'unicità in un ambiente multiutente. Il PRAGMA garantisce il mantenimento di code_control non inquina la transazione più ampia. Ci consente di chiamare la funzione in un trigger senza deadlock.

Ecco una tabella con una chiave come la tua:

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

Non ho niente nella manica prima di popolare 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> 

Quindi l'ovvia obiezione a questa implementazione è la scalabilità. Le transazioni di inserimento vengono serializzate su code_control tavolo. Questo è assolutamente vero. Tuttavia il blocco viene mantenuto per il minor tempo possibile, quindi questo non dovrebbe essere un problema anche se il t42 la tabella viene popolata molte volte al secondo.

Tuttavia, se la tabella è soggetta a un numero enorme di inserimenti simultanei, il blocco potrebbe diventare un problema. È fondamentale che la tabella disponga di slot di transazione interessati sufficienti (INITRANS, MAXTRANS) per far fronte alle richieste simultanee. Ma i sistemi molto impegnati potrebbero richiedere un'implementazione più intelligente (forse generando gli ID in batch); in caso contrario abbandonare la chiave composta a favore di una sequenza (perché le sequenze si ridimensionano in ambienti multiutente).