PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

postgresql génère une séquence sans espace

Les séquences ne génèrent pas d'ensembles de nombres sans espace, et il n'y a vraiment aucun moyen de leur faire faire cela car une annulation ou une erreur "utilisera" le numéro de séquence.

J'ai écrit un article à ce sujet il y a quelques temps. Il s'adresse à Oracle mais concerne en fait les principes fondamentaux des nombres sans écart, et je pense que la même chose s'applique ici.

Eh bien, c'est encore arrivé. Quelqu'un a demandé comment mettre en œuvre une exigence pour générer une série de chiffres sans espace et un essaim de nay-sayers est descendu sur eux pour dire (et ici je paraphrase légèrement) que cela tuera les performances du système, c'est rarement une exigence valide , que celui qui a écrit l'exigence est un idiot bla bla bla.

Comme je le souligne sur le fil, c'est parfois une véritable exigence légale de générer des séries de chiffres sans lacunes. Les numéros de facture des plus de 2 000 000 d'organisations au Royaume-Uni qui sont enregistrées à la TVA (taxe de vente) ont une telle exigence, et la raison en est assez évidente :il est plus difficile de cacher la génération de revenus aux autorités fiscales. J'ai vu des commentaires selon lesquels c'est une exigence en Espagne et au Portugal, et je ne serais pas surpris si ce n'était pas une exigence dans de nombreux autres pays.

Donc, si nous acceptons qu'il s'agisse d'une exigence valide, dans quelles circonstances les séries de nombres sans lacunes* posent-elles problème ? La pensée de groupe vous ferait souvent croire que c'est toujours le cas, mais en fait, ce n'est qu'un problème potentiel dans des circonstances très particulières.

  1. La série de chiffres ne doit pas comporter d'espace.
  2. Plusieurs processus créent les entités auxquelles le numéro est associé (par exemple, les factures).
  3. Les numéros doivent être générés au moment de la création de l'entité.

Si toutes ces exigences doivent être remplies, vous avez un point de sérialisation dans votre application, et nous en discuterons dans un instant.

Parlons d'abord des méthodes de mise en œuvre d'une exigence de série de chiffres si vous pouvez supprimer l'une de ces exigences.

Si votre série de nombres peut avoir des lacunes (et que vous avez plusieurs processus nécessitant une génération instantanée du nombre), utilisez un objet Oracle Sequence. Ils sont très performants et les situations dans lesquelles des lacunes peuvent être attendues ont été très bien discutées. Il n'est pas trop difficile de minimiser le nombre de numéros ignorés en faisant des efforts de conception pour minimiser le risque d'échec du processus entre la génération du numéro et la validation de la transaction, si cela est important.

Si vous n'avez pas plusieurs processus créant les entités (et que vous avez besoin d'une série de numéros sans lacunes qui doivent être générées instantanément), comme cela pourrait être le cas avec la génération par lots de factures, alors vous avez déjà un point de sérialisation. Cela en soi peut ne pas être un problème et peut être un moyen efficace d'effectuer l'opération requise. La génération des nombres sans espace est plutôt triviale dans ce cas. Vous pouvez lire la valeur maximale actuelle et appliquer une valeur incrémentielle à chaque entité avec un certain nombre de techniques. Par exemple, si vous insérez un nouveau lot de factures dans votre table de factures à partir d'une table de travail temporaire, vous pouvez :

insert into
  invoices
    (
    invoice#,
    ...)
with curr as (
  select Coalesce(Max(invoice#)) max_invoice#
  from   invoices)
select
  curr.max_invoice#+rownum,
  ...
from
  tmp_invoice
  ...

Bien sûr, vous protégeriez votre processus afin qu'une seule instance puisse s'exécuter à la fois (probablement avec DBMS_Lock si vous utilisez Oracle), et protégeriez la facture # avec une contrainte de clé unique, et vérifierez probablement les valeurs manquantes avec un code séparé si vous vous souciez vraiment, vraiment.

Si vous n'avez pas besoin d'une génération instantanée des numéros (mais vous en avez besoin sans interruption et plusieurs processus génèrent les entités), vous pouvez autoriser la génération des entités et la validation de la transaction, puis laisser la génération du numéro à un seul lot. travail. Une mise à jour sur la table des entités, ou une insertion dans une table séparée.

Donc, si nous avons besoin du tiercé gagnant de la génération instantanée d'une série de nombres sans intervalle par plusieurs processus ? Tout ce que nous pouvons faire est d'essayer de minimiser la période de sérialisation dans le processus, et je propose les conseils suivants, et j'accepte tout conseil supplémentaire (ou contre-conseil bien sûr).

  1. Stockez vos valeurs actuelles dans une table dédiée. N'UTILISEZ PAS de séquence.
  2. Assurez-vous que tous les processus utilisent le même code pour générer de nouveaux nombres en l'encapsulant dans une fonction ou une procédure.
  3. Sérialisez l'accès au générateur de nombres avec DBMS_Lock, en vous assurant que chaque série possède son propre verrou dédié.
  4. Maintenez le verrou dans le générateur de séries jusqu'à ce que votre transaction de création d'entité soit terminée en relâchant le verrou lors de la validation
  5. Retarder la génération du numéro jusqu'au dernier moment possible.
  6. Tenez compte de l'impact d'une erreur inattendue après la génération du numéro et avant la fin de la validation :l'application effectuera-t-elle une annulation correcte et libérera-t-elle le verrou, ou maintiendra-t-elle le verrou sur le générateur de série jusqu'à ce que la session se déconnecte plus tard ? Quelle que soit la méthode utilisée, si la transaction échoue, le ou les numéros de série doivent être « renvoyés au pool ».
  7. Pouvez-vous encapsuler le tout dans un déclencheur sur la table de l'entité ? Pouvez-vous l'encapsuler dans une table ou un autre appel d'API qui insère la ligne et valide l'insertion automatiquement ?

Article d'origine