sql >> Database >  >> RDS >> Sqlserver

Unieke samengestelde sleutel van SQL Server van twee velden met automatische verhoging van het tweede veld

Sinds iemand een soortgelijke vraag heeft gepost, zit ik hier over na te denken. Het eerste probleem is dat DB's geen "partitioneerbare" sequenties bieden (die zouden herstarten/onthouden op basis van verschillende sleutels). De tweede is dat de SEQUENCE objecten die zijn verstrekt, zijn gericht op snelle toegang en kunnen niet worden teruggedraaid (dat wil zeggen, u zal gaten krijgen). Dit sluit in wezen het gebruik van een ingebouwd hulpprogramma uit... wat betekent dat we onze eigen hulpprogramma's moeten gebruiken.

Het eerste dat we nodig hebben, is een tabel om onze volgnummers op te slaan. Dit kan vrij eenvoudig zijn:

CREATE TABLE Invoice_Sequence (base CHAR(1) PRIMARY KEY CLUSTERED,
                               invoiceNumber INTEGER);

In werkelijkheid is de base kolom moet een refererende-sleutelverwijzing zijn naar de tabel/id die de onderneming(en)/entiteiten definieert waarvoor u facturen uitgeeft. In deze tabel wilt u dat de boekingen uniek zijn per uitgegeven entiteit.

Vervolgens wil je een opgeslagen proces dat een sleutel nodig heeft (base ) en spuug het volgende nummer in de reeks uit (invoiceNumber ). De benodigde set sleutels varieert (dwz sommige factuurnummers moeten het jaar of de volledige datum van uitgifte bevatten), maar het basisformulier voor deze situatie is als volgt:

CREATE PROCEDURE Next_Invoice_Number @baseKey CHAR(1), 
                                     @invoiceNumber INTEGER OUTPUT 
AS MERGE INTO Invoice_Sequence Stored
              USING (VALUES (@baseKey)) Incoming(base)
                 ON Incoming.base = Stored.base
   WHEN MATCHED THEN UPDATE SET Stored.invoiceNumber = Stored.invoiceNumber + 1
   WHEN NOT MATCHED BY TARGET THEN INSERT (base) VALUES(@baseKey)
   OUTPUT INSERTED.invoiceNumber ;;

Merk op dat:

  1. Je moet voer dit uit in een geserialiseerde transactie
  2. De transactie moet dezelfde zijn die in de bestemmingstabel (factuur) wordt ingevoegd.

Dat klopt, je krijgt nog steeds blokkering per bedrijf bij het uitgeven van factuurnummers. Je kan niet vermijd dit als factuurnummers opeenvolgend moeten zijn, zonder gaten - totdat de rij daadwerkelijk is vastgelegd, kan deze worden teruggedraaid, wat betekent dat het factuurnummer niet zou zijn uitgegeven.

Nu, aangezien u niet wilt onthouden om de procedure voor het item aan te roepen, wikkel het in een trigger:

CREATE TRIGGER Populate_Invoice_Number ON Invoice INSTEAD OF INSERT
AS 
  DECLARE @invoiceNumber INTEGER
  BEGIN
    EXEC Next_Invoice_Number Inserted.base, @invoiceNumber OUTPUT
    INSERT INTO Invoice (base, invoiceNumber) 
                VALUES (Inserted.base, @invoiceNumber)
  END

(je hebt natuurlijk meer kolommen, waaronder andere die automatisch moeten worden ingevuld - je moet ze invullen)
...die je vervolgens kunt gebruiken door simpelweg te zeggen:

INSERT INTO Invoice (base) VALUES('A');

Dus wat hebben we gedaan? Meestal ging al dit werk over het verkleinen van het aantal rijen dat door een transactie werd vergrendeld. Tot deze INSERT is vastgelegd, zijn er slechts twee rijen vergrendeld:

  • De rij in Invoice_Sequence het volgnummer behouden
  • De rij in Invoice voor de nieuwe factuur.

Alle andere rijen voor een bepaalde base zijn gratis - ze kunnen naar believen worden bijgewerkt of opgevraagd (het verwijderen van informatie uit dit soort systemen maakt accountants vaak nerveus). U moet waarschijnlijk beslissen wat er moet gebeuren als vragen normaal gesproken de openstaande factuur bevatten...



  1. PostgreSQL LIKE prestatievariaties voor query's

  2. Hoe u met sqlalchemy dynamisch kunt binden aan de database-engine per verzoek

  3. Download een kopie van uw database

  4. Problemen oplossen:te veel omleidingen