sql >> Base de Datos >  >> RDS >> Oracle

Cómo implementar secuencias multidimensionales

La única manera de hacer esto es con una tabla de control 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 se mantiene así ...

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

Lo importante es SELECCIONAR... PARA ACTUALIZAR. El bloqueo pesimista garantiza la singularidad en un entorno multiusuario. PRAGMA garantiza que el mantenimiento de code_control no contamina la transacción más amplia. Nos permite llamar a la función en un disparador sin interbloqueos.

Aquí hay una tabla con una clave como la tuya:

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

No tengo nada bajo la manga antes de llenar 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> 

Entonces, la objeción obvia a esta implementación es la escalabilidad. Las transacciones de inserción se serializan en el code_control mesa. Eso es absolutamente cierto. Sin embargo, el bloqueo se mantiene durante el menor tiempo posible, por lo que esto no debería ser un problema incluso si t42 la tabla se llena muchas veces por segundo.

Sin embargo, si la mesa está sujeta a un gran número de inserciones simultáneas, el bloqueo puede convertirse en un problema. Es crucial que la mesa tenga suficientes espacios de transacciones interesadas (INITRANS, MAXTRANS) para hacer frente a las demandas concurrentes. Pero los sistemas muy ocupados pueden necesitar una implementación más inteligente (quizás generando las ID en lotes); de lo contrario, abandone la clave compuesta en favor de una secuencia (porque las secuencias escalan en entornos multiusuario).