sql >> Database >  >> RDS >> Oracle

Hoe multidimensionale reeksen te implementeren

De enige manier om dit te doen is met een codecontroletabel ...

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 zo wordt onderhouden ...

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

Het belangrijkste is de SELECT ... VOOR UPDATE. Pessimistische vergrendeling garandeert uniciteit in een omgeving met meerdere gebruikers. De PRAGMA zorgt ervoor dat het onderhouden van code_control vervuilt de bredere transactie niet. Het stelt ons in staat om de functie in een trigger aan te roepen zonder deadlocks.

Hier is een tabel met een sleutel zoals die van jou:

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

Ik heb niets meer in petto voordat ik t42 vul :

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> 

Het voor de hand liggende bezwaar tegen deze implementatie is dus schaalbaarheid. Transacties invoegen worden geserialiseerd op de code_control tafel. Dat is absoluut waar. Het slot wordt echter zo kort mogelijk vastgehouden, dus dit zou geen probleem moeten zijn, zelfs niet als de t42 tabel wordt vele malen per seconde gevuld.

Als de tafel echter wordt onderworpen aan enorme aantallen gelijktijdige inserties, kan de vergrendeling een probleem worden. Het is van cruciaal belang dat de tafel voldoende slots voor geïnteresseerde transacties heeft (INITRANS, MAXTRANS) om aan gelijktijdige eisen te kunnen voldoen. Maar erg drukke systemen hebben misschien een slimmere implementatie nodig (misschien genereren ze de ID's in batches); verlaat anders de samengestelde sleutel ten gunste van een sequentie (omdat sequenties schalen in omgevingen met meerdere gebruikers).




  1. Vergelijk twee MySQL-databases

  2. Wachtwoord reset systeem in PHP

  3. BDE vs ADO in Delphi

  4. Als ik verbinding maak met meerdere databases, heb ik dan meerdere SQLAlchemy Metadata-, Base- of Session-objecten nodig?