Das geht nur mit einer Code-Control-Tabelle ...
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 so gepflegt wird ...
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;
/
Wichtig ist das SELECT ... FOR UPDATE. Pessimistisches Sperren garantiert Eindeutigkeit in einer Umgebung mit mehreren Benutzern. Das PRAGMA stellt sicher, dass code_control
beibehalten wird verschmutzt nicht die umfassendere Transaktion. Es erlaubt uns, die Funktion in einem Trigger ohne Deadlocks aufzurufen.
Hier ist eine Tabelle mit einem Schlüssel wie Ihrem:
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;
/
Ich habe nichts im Ärmel, bevor ich t42
fülle :
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>
Der offensichtliche Einwand gegen diese Implementierung ist also die Skalierbarkeit. Das Einfügen von Transaktionen wird auf dem code_control
serialisiert Tisch. Das ist absolut wahr. Die Sperre wird jedoch so kurz wie möglich gehalten, sodass dies kein Problem sein sollte, selbst wenn t42
Tabelle wird viele Male pro Sekunde gefüllt.
Wenn die Tabelle jedoch einer großen Anzahl gleichzeitiger Einfügungen ausgesetzt ist, kann das Sperren zu einem Problem werden. Es ist entscheidend, dass die Tabelle über genügend Slots für interessierte Transaktionen (INITRANS, MAXTRANS) verfügt, um mit gleichzeitigen Anforderungen fertig zu werden. Aber sehr ausgelastete Systeme benötigen möglicherweise eine intelligentere Implementierung (vielleicht das Generieren der IDs in Stapeln); Andernfalls verzichten Sie auf den zusammengesetzten Schlüssel zugunsten einer Sequenz (da Sequenzen in Umgebungen mit mehreren Benutzern skalieren).