Korzystam z PostgreSQL, ale uważam, że większość najlepszych baz danych musi mieć podobne możliwości, a ponadto, że rozwiązania dla nich mogą inspirować rozwiązania dla mnie, więc nie rozważaj tego specyficznego dla PostgreSQL.
Wiem, że nie jestem pierwszym, który próbuje rozwiązać ten problem, więc myślę, że warto o to zapytać, ale staram się oszacować koszty modelowania danych księgowych, aby każda transakcja była zasadniczo zrównoważona. Dane księgowe są tylko do dodawania. Ogólne ograniczenie (zapisane w pseudokodzie) może wyglądać mniej więcej tak:
CREATE TABLE journal_entry (
id bigserial not null unique, --artificial candidate key
journal_type_id int references journal_type(id),
reference text, -- source document identifier, unique per journal
date_posted date not null,
PRIMARY KEY (journal_type_id, reference)
);
CREATE TABLE journal_line (
entry_id bigint references journal_entry(id),
account_id int not null references account(id),
amount numeric not null,
line_id bigserial not null unique,
CHECK ((sum(amount) over (partition by entry_id) = 0) -- this won't work
);
Oczywiście takie ograniczenie sprawdzania nigdy nie zadziała. Działa dla każdego wiersza i może sprawdzać całą bazę danych. Tak więc zawsze to zawiedzie i będzie powolne.
Więc moje pytanie brzmi: jaki jest najlepszy sposób modelowania tego ograniczenia? Do tej pory w zasadzie przyjrzałem się dwóm pomysłom. Zastanawiasz się, czy są to jedyne, czy ktoś ma lepszy sposób (inny niż pozostaw to na poziomie aplikacji lub przechowywanym proc).
- Mógłbym pożyczyć stronę ze świata rachunkowości dotyczącą różnicy między księgą oryginalnego wpisu a księgą końcowego wpisu (dziennik ogólny vs. księga główna). W związku z tym mógłbym zamodelować to jako tablicę wierszy dziennika dołączonych do wpisu dziennika, wymusić ograniczenie na tablicy (w terminach PostgreSQL wybierz sumę (ilość) = 0 z Unnest (je.line_items). Wyzwalacz może się rozwinąć i zapisz je w tabeli elementów zamówienia, gdzie można by łatwiej egzekwować ograniczenia poszczególnych kolumn, i gdzie indeksy itp. mogłyby być bardziej przydatne. W tym kierunku się opieram.
- Mógłbym spróbować zakodować wyzwalacz ograniczenia, który wymuszałby tę na transakcję, przy założeniu, że suma serii zer zawsze będzie wynosić 0.
Porównuję je z obecnym podejściem polegającym na wymuszaniu logiki w procedurze przechowywanej. Koszt złożoności porównuje się z ideą, że matematyczny dowód ograniczeń przewyższa testy jednostkowe. Główną wadą powyższego punktu 1 jest to, że typy jako krotki są jednym z tych obszarów w PostgreSQL, w których regularnie występują niespójne zachowania i zmiany w założeniach, dlatego miałbym nawet nadzieję, że zachowanie w tym obszarze może się z czasem zmienić. Projektowanie przyszłej bezpiecznej wersji nie jest takie łatwe.
Czy istnieją inne sposoby rozwiązania tego problemu, które skalują się do milionów rekordów w każdej tabeli? Czy coś brakuje? Czy brakuje mi kompromisu?
W odpowiedzi na poniższy punkt Craiga na temat wersji, będzie to musiało działać przynajmniej na PostgreSQL 9.2 i nowszych wersjach (może 9.1 i nowszych, ale prawdopodobnie możemy przejść do wersji 9.2).