sql >> Database teknologi >  >> RDS >> PostgreSQL

postgresql generere sekvens uden mellemrum

Sekvenser genererer ikke mellemrumsfri sæt af tal, og der er virkelig ingen måde at få dem til at gøre det, fordi en rollback eller fejl vil "bruge" sekvensnummeret.

Jeg skrev en artikel om dette for et stykke tid siden. Det er rettet mod Oracle, men handler i virkeligheden om de grundlæggende principper for hulfrie tal, og jeg tror, ​​det samme gælder her.

Nå, det er sket igen. Nogen har spurgt, hvordan man implementerer et krav om at generere en hulfri række af tal, og en sværm af nej-sigere er kommet ned over dem for at sige (og her omskriver jeg lidt), at dette vil dræbe systemets ydeevne, det er det sjældent et gyldigt krav , at den, der har skrevet kravet, er en idiot bla bla bla.

Som jeg påpeger i tråden, er det nogle gange et ægte lovkrav at generere hulfri rækker af tal. Fakturanumre for de 2.000.000+ organisationer i Storbritannien, der er momsregistreret (omsætningsafgift) har et sådant krav, og årsagen til dette er ret indlysende:at det gør det sværere at skjule genereringen af ​​indtægter fra skattemyndighederne. Jeg har set kommentarer om, at det er et krav i Spanien og Portugal, og jeg ville ikke blive overrasket, hvis det ikke var et krav i mange andre lande.

Så hvis vi accepterer, at det er et gyldigt krav, under hvilke omstændigheder er hulfri rækker* af tal et problem? Gruppetænkning ville ofte få dig til at tro, at det altid er det, men faktisk er det kun et potentielt problem under meget særlige omstændigheder.

  1. Talrækken må ikke have mellemrum.
  2. Flere processer skaber de enheder, som nummeret er knyttet til (f.eks. fakturaer).
  3. Numrene skal genereres på det tidspunkt, hvor entiteten oprettes.

Hvis alle disse krav skal være opfyldt, har du et punkt med serialisering i din ansøgning, og vi vil diskutere det om et øjeblik.

Lad os først tale om metoder til at implementere en serie-af-numre-krav, hvis du kan droppe et af disse krav.

Hvis din serie af tal kan have huller (og du har flere processer, der kræver øjeblikkelig generering af nummeret), så brug et Oracle Sequence-objekt. De er meget højtydende, og de situationer, hvor der kan forventes huller, er blevet diskuteret meget godt. Det er ikke for udfordrende at minimere mængden af ​​numre, der springes over, ved at lave designbestræbelser for at minimere chancen for en procesfejl mellem generering af nummeret og forpligtelse af transaktionen, hvis det er vigtigt.

Hvis du ikke har flere processer til at oprette entiteterne (og du har brug for en hulfri serie af tal, der skal genereres øjeblikkeligt), som det kan være tilfældet med batchgenerering af fakturaer, så har du allerede et punkt med serialisering. Det i sig selv er muligvis ikke et problem, og det kan være en effektiv måde at udføre den nødvendige operation på. Generering af de hulfrie tal er ret trivielt i dette tilfælde. Du kan læse den aktuelle maksimale værdi og anvende en stigende værdi på hver enhed med en række teknikker. Hvis du f.eks. indsætter et nyt parti fakturaer i din fakturatabel fra en midlertidig arbejdstabel, kan du:

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

Selvfølgelig ville du beskytte din proces, så kun én instans kan køre ad gangen (sandsynligvis med DBMS_Lock, hvis du bruger Oracle), og beskytte faktura# med en unik nøglekontrain, og sandsynligvis kontrollere for manglende værdier med separat kode, hvis du er virkelig, virkelig ligeglad.

Hvis du ikke har brug for øjeblikkelig generering af numrene (men du har brug for dem uden hul, og flere processer genererer entiteterne), kan du tillade, at enhederne genereres og transaktionen forpligtes, og derefter overlade genereringen af ​​nummeret til en enkelt batch job. En opdatering af entitetstabellen eller en indsættelse i en separat tabel.

Så hvis vi har brug for trifectaen af ​​øjeblikkelig generering af en hulfri række af tal ved flere processer? Alt, hvad vi kan gøre, er at forsøge at minimere perioden med serialisering i processen, og jeg tilbyder følgende råd og hilser alle yderligere råd (eller modråd naturligvis) velkommen.

  1. Gem dine aktuelle værdier i en dedikeret tabel. Brug IKKE en sekvens.
  2. Sørg for, at alle processer bruger den samme kode til at generere nye tal ved at indkapsle den i en funktion eller procedure.
  3. Serialiser adgangen til nummergeneratoren med DBMS_Lock, og sørg for, at hver serie har sin egen dedikerede lås.
  4. Hold låsen i seriegeneratoren, indtil din enhedsoprettelsestransaktion er fuldført ved at frigive låsen ved commit
  5. Udskyd genereringen af ​​nummeret til det sidste mulige øjeblik.
  6. Overvej virkningen af ​​en uventet fejl efter generering af nummeret og før commit er fuldført – vil applikationen rulle tilbage elegant og frigive låsen, eller vil den holde låsen på seriegeneratoren, indtil sessionen afbrydes senere? Uanset hvilken metode der bruges, hvis transaktionen mislykkes, skal serienumrene "returneres til puljen".
  7. Kan du indkapsle det hele i en trigger på entitetens bord? Kan du indkapsle det i en tabel eller et andet API-kald, der indsætter rækken og begår indsættelsen automatisk?

Original artikel



  1. Hvordan finder man de dårligst ydende forespørgsler i SQL Server 2008?

  2. Hvordan opretter jeg en lagret procedure, der valgfrit vil søge i kolonner?

  3. Hvordan kan jeg kontrollere MySQL-motortypen for en specifik tabel?

  4. Matcher alle værdier i IN-sætning