Вы не можете использовать последовательность для этого. Вам нужна единственная точка сериализации, через которую все вставки должны уйти - иначе атрибут «без щелей» не может быть гарантирован. Вы также должны убедиться, что ни одна строка не будет удалена из этой таблицы.
Сериализация также означает, что только одна транзакция может вставлять строки в эту таблицу - все остальные вставки должны ждать, пока «предыдущая» вставка не будет выполнена или откат.
Один образец, который может быть реализован, состоит в том, чтобы иметь таблицу, в которой хранятся номера «последовательности». Предположим, нам нужно это для номеров счетов, которые должны быть беспроблемными по юридическим причинам.
Таким образом, мы сначала создать таблицу для хранения «текущее значение»:
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');
Новый номер генерируется. A второй транзакции тогда нужно дождаться, пока первая вставка не будет завершена или откатана.
Как я уже сказал, это решение не является масштабируемым. Не за что. Это значительно замедлит ваше приложение, если в эту таблицу будет много вставок. Но вы не можете иметь оба: масштабируемые и правильной реализации бесщелевой последовательности.
Я также уверен, что есть краевой кейс, который не подпадает под вышеуказанный код. Поэтому довольно вероятно, что вы все еще можете столкнуться с пробелами.
«Короче говоря, я хочу сохранить значения последовательности при откате *» - почему? Это звучит как полная трата времени и ресурсов. И это определенно не решение, если вам действительно нужны бесконечные последовательности. И _if_ вам *** действительно нужны бесконечные последовательности (например, по юридическим причинам), чем «последовательность» **, а не ** правильный инструмент для этого. –
Мне действительно нужны бесцельные последовательности – Vish021
@a_horse_with_no_name или есть способ получить текущее имя таблицы в транзакции? – Vish021