Odkąd ktoś zadał podobne pytanie, zastanawiałem się nad tym. Pierwszym problemem jest to, że DB nie dostarczają „partycjonowanych” sekwencji (które mogłyby się zrestartować/zapamiętać w oparciu o różne klucze). Po drugie, SEQUENCE
obiekty, które są są nastawione na szybki dostęp i nie można ich cofnąć (tzn. będziesz uzyskać luki). To zasadniczo wyklucza korzystanie z wbudowanego narzędzia... co oznacza, że musimy rozwijać własne.
Pierwszą rzeczą, której będziemy potrzebować, jest tabela do przechowywania naszych numerów sekwencyjnych. To może być dość proste:
CREATE TABLE Invoice_Sequence (base CHAR(1) PRIMARY KEY CLUSTERED,
invoiceNumber INTEGER);
W rzeczywistości base
kolumna powinna być odniesieniem do klucza obcego do dowolnej tabeli/identyfikatora definiującej firmę/podmioty, dla których wystawiasz faktury. W tej tabeli chcesz, aby wpisy były unikalne dla wystawionej jednostki.
Następnie potrzebujesz zapisanego proc, które zajmie klucz (base
) i wypluj następną liczbę w sekwencji (invoiceNumber
). Zestaw niezbędnych kluczy będzie się różnić (tj. niektóre numery faktur muszą zawierać rok lub pełną datę wystawienia), ale podstawowy formularz dla tej sytuacji jest następujący:
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 ;;
Pamiętaj, że:
- Trze musisz uruchom to w serializowanej transakcji
- Transakcja musi być tym samym, który jest wstawiany do tabeli docelowej (faktury).
Zgadza się, nadal będziesz blokować poszczególne firmy podczas wystawiania numerów faktur. Nie możesz unikaj tego, jeśli numery faktur muszą być sekwencyjne, bez przerw - dopóki wiersz nie zostanie faktycznie zatwierdzony, może zostać wycofany, co oznacza, że numer faktury nie zostałby wystawiony.
Teraz, ponieważ nie chcesz pamiętać o wywołaniu procedury dla wpisu, zapakuj go w wyzwalacz:
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
(oczywiście masz więcej kolumn, w tym inne, które powinny być wypełniane automatycznie - musisz je wypełnić)
...których możesz użyć, mówiąc po prostu:
INSERT INTO Invoice (base) VALUES('A');
Więc co zrobiliśmy? Cała ta praca polegała głównie na zmniejszeniu liczby wierszy zablokowanych przez transakcję. Do tego INSERT
jest zatwierdzone, zablokowane są tylko dwa wiersze:
- Wiersz w
Invoice_Sequence
utrzymywanie numeru sekwencyjnego - Wiersz w
Invoice
dla nowej faktury.
Wszystkie inne wiersze dla określonej base
są bezpłatne - można je dowolnie aktualizować lub odpytywać (usuwanie informacji z tego rodzaju systemu często denerwuje księgowych). Prawdopodobnie musisz zdecydować, co powinno się stać, gdy zapytania zwykle zawierają oczekującą fakturę...