sql >> Database >  >> RDS >> PostgreSQL

postgresql genereert een reeks zonder tussenruimte

Reeksen genereren geen reeksen getallen zonder onderbrekingen, en er is echt geen manier om ze dat te laten doen, omdat een rollback of fout het volgnummer zal "gebruiken".

Ik heb hier een tijdje geleden een artikel over geschreven. Het is gericht op Oracle, maar gaat echt over de fundamentele principes van gap-free getallen, en ik denk dat hetzelfde hier geldt.

Nou, het is weer gebeurd. Iemand heeft gevraagd hoe een vereiste moet worden geïmplementeerd om een ​​reeks getallen zonder onderbrekingen te genereren en een zwerm nee-zeggers is op hen neergedaald om te zeggen (en hier parafraseer ik een beetje) dat dit de systeemprestaties zal schaden, dat is dat het zelden een geldige vereiste is , dat degene die de eis heeft geschreven een idioot is, bla bla.

Zoals ik in de thread al aangeef, is het soms een echte wettelijke vereiste om reeksen getallen zonder onderbrekingen te genereren. Factuurnummers voor de 2.000.000+ organisaties in het VK die btw-geregistreerd zijn, hebben een dergelijke vereiste, en de reden hiervoor is nogal duidelijk:dat het moeilijker wordt om het genereren van inkomsten voor de belastingdienst te verbergen. Ik heb opmerkingen gezien dat het een vereiste is in Spanje en Portugal, en het zou me niet verbazen als het in veel andere landen niet verplicht was.

Dus, als we accepteren dat het een geldige vereiste is, onder welke omstandigheden zijn gap-free reeksen* van getallen dan een probleem? Groepsdenken zou je vaak doen geloven dat dit altijd zo is, maar in feite is het alleen een potentieel probleem onder zeer specifieke omstandigheden.

  1. De reeks getallen mag geen gaten bevatten.
  2. Meerdere processen creëren de entiteiten waaraan het nummer is gekoppeld (bijv. facturen).
  3. De nummers moeten worden gegenereerd op het moment dat de entiteit wordt gemaakt.

Als aan al deze vereisten moet worden voldaan, heeft u een punt van serialisatie in uw toepassing en we zullen dat zo bespreken.

Laten we het eerst hebben over methoden om een ​​reeks van getallen te implementeren als u een van die vereisten kunt laten vallen.

Als uw reeks getallen hiaten kan hebben (en u hebt meerdere processen die onmiddellijke generatie van het nummer vereisen), gebruik dan een Oracle Sequence-object. Ze presteren zeer goed en de situaties waarin hiaten kunnen worden verwacht, zijn zeer goed besproken. Het is niet al te uitdagend om het aantal overgeslagen nummers te minimaliseren door ontwerpinspanningen te doen om de kans op een procesfout tussen het genereren van het nummer en het uitvoeren van de transactie te minimaliseren, als dat belangrijk is.

Als u niet meerdere processen hebt om de entiteiten te creëren (en u een reeks van nummers zonder onderbrekingen nodig hebt die onmiddellijk moet worden gegenereerd), zoals het geval kan zijn bij het batchgewijs genereren van facturen, dan heeft u al een punt van serialisatie. Dat is op zich misschien geen probleem en kan een efficiënte manier zijn om de vereiste bewerking uit te voeren. Het genereren van de gap-free nummers is in dit geval nogal triviaal. Met een aantal technieken kunt u de huidige maximale waarde uitlezen en op elke entiteit een oplopende waarde toepassen. Als u bijvoorbeeld een nieuwe batch facturen in uw factuurtabel invoegt vanuit een tijdelijke werktabel, kunt u:

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

Natuurlijk zou u uw proces beschermen zodat slechts één instantie tegelijk kan worden uitgevoerd (waarschijnlijk met DBMS_Lock als u Oracle gebruikt), en de factuur # beschermen met een unieke sleutelbeperking, en waarschijnlijk controleren op ontbrekende waarden met afzonderlijke code als het kan je echt heel erg schelen.

Als u geen onmiddellijke generatie van de nummers nodig hebt (maar u hebt ze nodig zonder tussenruimte en meerdere processen genereren de entiteiten), dan kunt u toestaan ​​dat de entiteiten worden gegenereerd en de transactie wordt vastgelegd, en vervolgens het genereren van het nummer overlaten aan een enkele batch functie. Een update van de entiteitstabel, of een invoeging in een aparte tabel.

Dus als we de trifecta nodig hebben van het onmiddellijk genereren van een reeks getallen zonder onderbrekingen door meerdere processen? Het enige wat we kunnen doen is proberen de periode van serialisatie in het proces te minimaliseren, en ik bied het volgende advies, en verwelkom elk aanvullend advies (of tegenadvies natuurlijk).

  1. Sla uw huidige waarden op in een speciale tabel. Gebruik GEEN reeks.
  2. Zorg ervoor dat alle processen dezelfde code gebruiken om nieuwe getallen te genereren door deze in een functie of procedure in te kapselen.
  3. Serialiseer toegang tot de nummergenerator met DBMS_Lock, zorg ervoor dat elke serie zijn eigen speciale slot heeft.
  4. Houd de vergrendeling in de seriegenerator vast totdat uw transactie voor het maken van entiteiten is voltooid door de vergrendeling op de vastlegging los te laten
  5. Stel het genereren van het nummer uit tot het laatst mogelijke moment.
  6. Overweeg de impact van een onverwachte fout na het genereren van het nummer en voordat de vastlegging is voltooid - zal de toepassing gracieus terugdraaien en de vergrendeling opheffen, of zal deze de vergrendeling op de seriegenerator vasthouden totdat de sessie later wordt verbroken? Welke methode ook wordt gebruikt, als de transactie mislukt, moeten de serienummers worden "teruggegeven aan de pool".
  7. Kun je het hele ding in een trigger inkapselen op de tabel van de entiteit? Kun je het inkapselen in een tabel of een andere API-aanroep die de rij invoegt en de invoeging automatisch vastlegt?

Origineel artikel



  1. Een database exporteren met behulp van de opdrachtregel

  2. Hoe een door de gebruiker gedefinieerde uitzondering te declareren met behulp van een uitzonderingsvariabele in Oracle Database?

  3. JOIN (SELECT ... ) ue ON 1=1?

  4. Hoe krijg ik SQL-tekst van Postgres-gebeurtenistrigger