As sequências não geram conjuntos de números livres de lacunas, e realmente não há como fazê-los fazer isso porque uma reversão ou erro "usará" o número de sequência.
Eu escrevi um artigo sobre isso um tempo atrás. Ele é direcionado à Oracle, mas é realmente sobre os princípios fundamentais de números livres de lacunas, e acho que o mesmo se aplica aqui.
Pois é, aconteceu de novo. Alguém perguntou como implementar um requisito para gerar uma série de números sem lacunas e um enxame de opositores desceu sobre eles para dizer (e aqui parafraseei um pouco) que isso matará o desempenho do sistema, raramente é um requisito válido , que quem escreveu o requisito é um idiota blá blá blá.
Como apontei no tópico, às vezes é um requisito legal genuíno gerar séries de números sem lacunas. Os números de fatura para as mais de 2.000.000 organizações no Reino Unido que são registradas no IVA (imposto sobre vendas) têm esse requisito, e a razão para isso é bastante óbvia:torna mais difícil ocultar a geração de receita das autoridades fiscais. Vi comentários de que é uma exigência na Espanha e em Portugal, e não ficaria surpreso se não fosse uma exigência em muitos outros países.
Então, se aceitarmos que é um requisito válido, em que circunstâncias as séries* de números sem lacunas são um problema? O pensamento de grupo muitas vezes faz você acreditar que sempre é, mas na verdade é apenas um problema potencial em circunstâncias muito particulares.
- A série de números não deve ter lacunas.
- Vários processos criam as entidades às quais o número está associado (por exemplo, faturas).
- Os números devem ser gerados no momento da criação da entidade.
Se todos esses requisitos precisarem ser atendidos, você terá um ponto de serialização em seu aplicativo e discutiremos isso em breve.
Primeiro, vamos falar sobre métodos de implementação de um requisito de série de números, se você puder descartar qualquer um desses requisitos.
Se sua série de números puder ter lacunas (e você tiver vários processos exigindo geração instantânea do número), use um objeto Oracle Sequence. Eles têm um desempenho muito alto e as situações em que as lacunas podem ser esperadas foram muito bem discutidas. Não é muito desafiador minimizar a quantidade de números ignorados fazendo esforços de design para minimizar a chance de uma falha de processo entre a geração do número e a confirmação da transação, se isso for importante.
Se você não tiver vários processos criando as entidades (e precisar de uma série de números sem lacunas que devem ser geradas instantaneamente), como pode ser o caso da geração de faturas em lote, você já tem um ponto de serialização. Isso por si só pode não ser um problema e pode ser uma maneira eficiente de realizar a operação necessária. Gerar os números livres de lacunas é bastante trivial neste caso. Você pode ler o valor máximo atual e aplicar um valor de incremento a cada entidade com várias técnicas. Por exemplo, se você estiver inserindo um novo lote de faturas em sua tabela de faturas a partir de uma tabela de trabalho temporária, você pode:
insert into
invoices
(
invoice#,
...)
with curr as (
select Coalesce(Max(invoice#)) max_invoice#
from invoices)
select
curr.max_invoice#+rownum,
...
from
tmp_invoice
...
É claro que você protegeria seu processo para que apenas uma instância pudesse ser executada por vez (provavelmente com DBMS_Lock se estiver usando Oracle) e protegeria o invoice# com uma restrição de chave exclusiva e provavelmente verificaria valores ausentes com código separado se você realmente se importa.
Se você não precisa de geração instantânea dos números (mas você precisa deles sem lacunas e vários processos geram as entidades), você pode permitir que as entidades sejam geradas e a transação confirmada e, em seguida, deixar a geração do número para um único lote trabalho. Uma atualização na tabela de entidades ou uma inserção em uma tabela separada.
Então, se precisarmos do trio de geração instantânea de uma série de números sem lacunas por vários processos? Tudo o que podemos fazer é tentar minimizar o período de serialização no processo, e ofereço o seguinte conselho e aceito qualquer conselho adicional (ou contra-conselho, é claro).
- Armazene seus valores atuais em uma tabela dedicada. NÃO use uma sequência.
- Certifique-se de que todos os processos usem o mesmo código para gerar novos números, encapsulando-o em uma função ou procedimento.
- Serialize o acesso ao gerador de números com DBMS_Lock, garantindo que cada série tenha seu próprio bloqueio dedicado.
- Segure o bloqueio no gerador de série até que sua transação de criação de entidade seja concluída, liberando o bloqueio na confirmação
- Atrase a geração do número até o último momento possível.
- Considere o impacto de um erro inesperado após a geração do número e antes da conclusão da confirmação — o aplicativo reverterá normalmente e liberará o bloqueio ou manterá o bloqueio no gerador de série até que a sessão seja desconectada posteriormente? Qualquer que seja o método usado, se a transação falhar, o(s) número(s) de série deve(m) ser “retornado(s) ao pool”.
- Você pode encapsular tudo em um gatilho na tabela da entidade? Você pode encapsulá-lo em uma tabela ou outra chamada de API que insere a linha e confirma a inserção automaticamente?
Artigo original