Você não pode usar uma sequência para isso. Você precisa de um único ponto de serialização por meio do qual todos inserções têm que ir - caso contrário, o atributo "gapless" não pode ser garantido. Você também precisa garantir que nenhuma linha seja excluída dessa tabela.
A serialização também significa que apenas uma única transação pode inserir linhas nessa tabela - todas as outras inserções precisam esperar até que a inserção "anterior" seja confirmada ou revertida.
Um padrão de como isso pode ser implementado é ter uma tabela onde os números da "sequência" são armazenados. Vamos supor que precisamos disso para números de faturas que precisam ser preenchidos por motivos legais.
Então, primeiro criamos a tabela para armazenar o "valor atual":
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');
Agora precisamos de uma função que gere o próximo número, mas que garanta que duas transações não possam obter o próximo número ao mesmo tempo.
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;
A função incrementará o contador e retornará o valor incrementado como resultado. Devido à
update
a linha da sequência agora está bloqueada e nenhuma outra transação pode atualizar esse valor. Se a transação de chamada for revertida, a atualização do contador de sequência também será. Se for confirmado, o novo valor será persistido. Para garantir que todos transação usa a função, um gatilho deve ser criado.
Crie a tabela em questão:
create table invoice
(
invoice_number integer not null primary key,
customer_id integer not null,
due_date date not null
);
Agora crie a função de gatilho e o gatilho:
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();
Agora, se uma transação faz isso:
insert into invoice (customer_id, due_date)
values (42, date '2015-12-01');
O novo número é gerado. Um segundo A transação precisa esperar até que a primeira inserção seja confirmada ou revertida.
Como eu disse:esta solução não é escalável. De jeito nenhum. Isso tornará seu aplicativo muito lento se houver muitas inserções nessa tabela. Mas você não pode ter os dois:um e escalável implementação correta de uma sequência sem intervalos.
Também tenho certeza de que existem casos extremos que não são cobertos pelo código acima. Portanto, é bem provável que você ainda possa acabar com lacunas.