sql >> Databasteknik >  >> RDS >> Oracle

Hur man implementerar flerdimensionella sekvenser

Det enda sättet att göra detta är med en kodkontrolltabell ...

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
/   

... som upprätthålls så här ...

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

Det viktiga är VÄLJ ... FÖR UPPDATERING. Pessimistisk låsning garanterar unikhet i en miljö med flera användare. PRAGMA säkerställer att upprätthålla code_control förorenar inte den bredare transaktionen. Det gör att vi kan anropa funktionen i en trigger utan dödläge.

Här är en tabell med en nyckel som din:

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

Jag har inget kvar innan jag fyller i 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> 

Så den uppenbara invändningen mot denna implementering är skalbarhet. Infogningstransaktioner serialiseras på code_control tabell. Det är helt sant. Låset hålls dock kvar under kortast möjliga tid, så detta bör inte vara ett problem även om t42 tabellen fylls i många gånger i sekunden.

Men om bordet utsätts för ett enormt antal samtidiga insatser kan låsningen bli ett problem. Det är avgörande att bordet har tillräckligt många intressetransaktionsplatser (INITRANS, MAXTRANS) för att klara av samtidiga krav. Men mycket upptagna system kan behöva en smartare implementering (kanske generera ID:n i omgångar); I annat fall överge den sammansatta nyckeln till förmån för en sekvens (eftersom sekvenser skalas i fleranvändarmiljöer).




  1. Hur konverterar man MySQL-tabellen till JSON med PHP?

  2. Hur implementerar man batchhämtning med Fluent NHibernate när man arbetar med Oracle?

  3. Jag behöver ovanliga mysql-resultat

  4. Förstå resultatet av Execute Explain Plan i Oracle SQL Developer