Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Como implementar sequências multidimensionais


A única maneira de fazer isso é com uma tabela de controle de código ...
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
/   

... que é mantido assim ...
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;
/

O importante é o SELECT... FOR UPDATE. O bloqueio pessimista garante exclusividade em um ambiente multiusuário. O PRAGMA garante que a manutenção do code_control não polui a transação mais ampla. Ele nos permite chamar a função em um gatilho sem deadlocks.

Aqui está uma tabela com uma chave como a sua:
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;
/

Não há nada na manga antes de eu preencher 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> 

Portanto, a objeção óbvia a essa implementação é a escalabilidade. As transações de inserção são serializadas no code_control tabela. Isso é absolutamente verdade. No entanto, o bloqueio é mantido pelo menor tempo possível, portanto, isso não deve ser um problema, mesmo que o t42 tabela é preenchida muitas vezes por segundo.

No entanto, se a tabela estiver sujeita a um grande número de inserções simultâneas, o travamento pode se tornar um problema. É crucial que a tabela tenha slots de transações de interesse (INITRANS, MAXTRANS) suficientes para lidar com demandas simultâneas. Mas sistemas muito ocupados podem precisar de uma implementação mais inteligente (talvez gerando os IDs em lotes); caso contrário, abandone a chave composta em favor de uma sequência (porque as sequências são dimensionadas em ambientes multiusuário).