Sekvenser genererar inte gapfria uppsättningar av nummer, och det finns verkligen inget sätt att få dem att göra det eftersom en rollback eller ett fel kommer att "använda" sekvensnumret.
Jag skrev en artikel om detta för ett tag sedan. Det är riktat till Oracle men handlar egentligen om de grundläggande principerna för gapfria nummer, och jag tror att samma sak gäller här.
Nåväl, det har hänt igen. Någon har frågat hur man implementerar ett krav på att generera en sifferserie utan luckor och en svärm av nej-sägare har kommit över dem för att säga (och här parafraserar jag något) att detta kommer att döda systemets prestanda, det är sällan ett giltigt krav , att den som skrivit kravet är en idiot bla bla bla.
Som jag påpekar i tråden är det ibland ett genuint lagkrav att generera gapfria nummerserier. Fakturanummer för de 2 000 000+ organisationer i Storbritannien som är momsregistrerade (omsättningsskatt) har ett sådant krav, och anledningen till detta är ganska uppenbar:att det gör det svårare att dölja genereringen av intäkter från skattemyndigheterna. Jag har sett kommentarer om att det är ett krav i Spanien och Portugal, och jag skulle inte bli förvånad om det inte var ett krav i många andra länder.
Så, om vi accepterar att det är ett giltigt krav, under vilka omständigheter är gapfria serier* av nummer ett problem? Grupptänkande skulle ofta få dig att tro att det alltid är det, men i själva verket är det bara ett potentiellt problem under mycket speciella omständigheter.
- Sifferserien får inte ha några luckor.
- Flera processer skapar de enheter som numret är kopplat till (t.ex. fakturor).
- Numren måste genereras vid den tidpunkt då enheten skapas.
Om alla dessa krav måste uppfyllas har du en serialiseringspunkt i din ansökan, och vi kommer att diskutera det om ett ögonblick.
Låt oss först prata om metoder för att implementera ett krav på serier om du kan släppa något av dessa krav.
Om din nummerserie kan ha luckor (och du har flera processer som kräver omedelbar generering av numret) använd då ett Oracle Sequence-objekt. De är mycket högpresterande och de situationer där luckor kan förväntas har diskuterats mycket väl. Det är inte alltför utmanande att minimera antalet siffror som hoppas över genom att göra designansträngningar för att minimera risken för ett processfel mellan generering av numret och genomförandet av transaktionen, om det är viktigt.
Om du inte har flera processer som skapar entiteterna (och du behöver en sifferserie utan luckor som måste genereras omedelbart), vilket kan vara fallet med batchgenerering av fakturor, så har du redan en serialiseringspunkt. Det i sig kanske inte är ett problem och kan vara ett effektivt sätt att utföra den nödvändiga operationen. Att generera de gapfria siffrorna är ganska trivialt i det här fallet. Du kan läsa det aktuella maxvärdet och tillämpa ett ökande värde på varje enhet med ett antal tekniker. Om du till exempel infogar ett nytt parti fakturor i din fakturatabell från en tillfällig arbetstabell 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
...
Naturligtvis skulle du skydda din process så att endast en instans kan köras åt gången (troligtvis med DBMS_Lock om du använder Oracle), och skydda fakturan# med en unik nyckelkontrainst, och förmodligen kontrollera om det saknas värden med separat kod om du bryr dig verkligen, verkligen.
Om du inte behöver omedelbar generering av numren (men du behöver dem utan luckor och flera processer genererar enheterna) kan du tillåta att enheterna genereras och transaktionen commiteras, och sedan överlåta genereringen av numret till en enda batch jobb. En uppdatering av entitetstabellen, eller en infogning i en separat tabell.
Så om vi behöver trifekta av omedelbar generering av en gapfri serie av tal genom flera processer? Allt vi kan göra är att försöka minimera serialiseringsperioden i processen, och jag ger följande råd och välkomnar alla ytterligare råd (eller motråd förstås).
- Lagra dina aktuella värden i en särskild tabell. ANVÄND INTE en sekvens.
- Se till att alla processer använder samma kod för att generera nya tal genom att kapsla in den i en funktion eller procedur.
- Serialisera åtkomst till nummergeneratorn med DBMS_Lock, och se till att varje serie har sitt eget dedikerade lås.
- Håll låset i seriegeneratorn tills transaktionen för att skapa entitet är klar genom att släppa låset vid commit
- Fröja genereringen av numret till sista möjliga ögonblick.
- Tänk på effekten av ett oväntat fel efter att numret har genererats och innan bekräftelsen är slutförd — kommer programmet att återställas på ett elegant sätt och släppa låset, eller kommer det att hålla låset på seriegeneratorn tills sessionen kopplas från senare? Oavsett vilken metod som används, om transaktionen misslyckas så måste serienumren "återföras till poolen".
- Kan du kapsla in det hela i en trigger på enhetens bord? Kan du kapsla in det i en tabell eller annat API-anrop som infogar raden och commiterar infogningen automatiskt?
Originalartikel