K tomu nelze použít sekvenci. Potřebujete jeden serializační bod, přes který vše vložky musí pryč - jinak nelze zaručit atribut "gapless". Musíte se také ujistit, že z této tabulky nebudou nikdy odstraněny žádné řádky.
Serializace také znamená, že pouze jedna transakce může vložit řádky do této tabulky - všechny ostatní vložení musí počkat, dokud nebude "předchozí" vložení potvrzeno nebo vráceno zpět.
Jedním ze vzorů, jak to lze implementovat, je mít tabulku, kde jsou uložena „sekvenční“ čísla. Předpokládejme, že to potřebujeme pro čísla faktur, která musí být z právních důvodů bez mezer.
Nejprve tedy vytvoříme tabulku, která bude obsahovat "aktuální hodnotu":
create table slow_sequence
(
seq_name varchar(100) not null primary key,
current_value integer not null default 0
);
-- create a "sequence" for invoices
insert into slow_sequence values ('invoice');
Nyní potřebujeme funkci, která vygeneruje další číslo, ale která zaručí, že žádné dvě transakce nemohou získat další číslo současně.
create or replace function next_number(p_seq_name text)
returns integer
as
$$
update slow_sequence
set current_value = current_value + 1
where seq_name = p_seq_name
returning current_value;
$$
language sql;
Funkce zvýší čítač a jako výsledek vrátí zvýšenou hodnotu. Kvůli update
řádek pro sekvenci je nyní uzamčen a žádná jiná transakce nemůže tuto hodnotu aktualizovat. Pokud je volající transakce vrácena zpět, je vrácena i aktualizace čítače sekvencí. Pokud je potvrzena, nová hodnota zůstane zachována.
Aby bylo zajištěno, že každý transakce používá funkci, měl by být vytvořen trigger.
Vytvořte příslušnou tabulku:
create table invoice
(
invoice_number integer not null primary key,
customer_id integer not null,
due_date date not null
);
Nyní vytvořte funkci spouštění a spouštěč:
create or replace function f_invoice_trigger()
returns trigger
as
$$
begin
-- the number is assigned unconditionally so that this can't
-- be prevented by supplying a specific number
new.invoice_number := next_number('invoice');
return new;
end;
$$
language plpgsql;
create trigger invoice_trigger
before insert on invoice
for each row
execute procedure f_invoice_trigger();
Nyní, pokud jedna transakce dělá toto:
insert into invoice (customer_id, due_date)
values (42, date '2015-12-01');
Vygeneruje se nové číslo. sekundu transakce pak musí počkat, dokud nebude první vložení potvrzeno nebo vráceno zpět.
Jak jsem řekl:toto řešení není škálovatelné. Vůbec ne. Masivně to zpomalí vaši aplikaci, pokud je v této tabulce mnoho příloh. Ale nemůžete mít obojí:škálovatelné a správná implementace sekvence bez mezer.
Jsem si také docela jistý, že existují okrajové případy, na které se výše uvedený kód nevztahuje. Je tedy docela pravděpodobné, že stále můžete mít mezery.