Не можете да използвате последователност за това. Имате нужда от една точка на сериализация, през която всички вложките трябва да отидат - в противен случай атрибутът "без празнини" не може да бъде гарантиран. Трябва също така да се уверите, че няма да бъдат изтрити редове от тази таблица.
Сериализацията също така означава, че само една транзакция може да вмъкне редове в тази таблица - всички останали вмъквания трябва да изчакат, докато „предишната“ вмъкване не бъде записана или върната назад.
Един модел как това може да бъде реализирано е да имате таблица, където се съхраняват "поредните" номера. Да предположим, че имаме нужда от това за номера на фактури, които трябва да са без празнини по правни причини.
Така че първо създаваме таблицата, която съдържа "текущата стойност":
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');
Сега имаме нужда от функция, която ще генерира следващото число, но която гарантира, че две транзакции не могат да получат следващото число по едно и също време.
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;
Функцията ще увеличи брояча и ще върне увеличената стойност като резултат. Поради update
редът за последователността вече е заключен и никоя друга транзакция не може да актуализира тази стойност. Ако транзакцията за повикване е върната назад, актуализацията на брояча на последователностите също. Ако е ангажиран, новата стойност се запазва.
За да сте сигурни, че вской транзакцията използва функцията, трябва да се създаде тригер.
Създайте въпросната таблица:
create table invoice
(
invoice_number integer not null primary key,
customer_id integer not null,
due_date date not null
);
Сега създайте функцията за задействане и тригера:
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();
Сега, ако една транзакция направи това:
insert into invoice (customer_id, due_date)
values (42, date '2015-12-01');
Новият номер се генерира. Втора след това транзакцията трябва да изчака, докато първото вмъкване бъде заето или се върне обратно.
Както казах:това решение не е мащабируемо. Въобще не. Това ще забави значително вашето приложение, ако има много вмъквания в тази таблица. Но не можете да имате и двете:мащабируеми правилно изпълнение на последователност без пропуски.
Също така съм почти сигурен, че има крайни случаи, които не са обхванати от горния код. Така че е много вероятно все още да имате пропуски.