Postgresql
交易、參考以及如何執行複式記賬?(PG)
複式記賬是
在財務會計系統中記錄財務資訊的一套規則,其中每筆交易或事件都會改變至少兩個不同的名義分類賬。
一個帳戶可以“借記”或“貸記”,所有貸記的總和必須等於所有借記的總和。
您將如何在 Postgres 數據庫中實現這一點?指定以下 DDL:
CREATE TABLE accounts( account_id serial NOT NULL PRIMARY KEY, account_name varchar(64) NOT NULL ); CREATE TABLE transactions( transaction_id serial NOT NULL PRIMARY KEY, transaction_date date NOT NULL ); CREATE TABLE transactions_details( id serial8 NOT NULL PRIMARY KEY, transaction_id integer NOT NULL REFERENCES transactions (transaction_id) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, account_id integer NOT NULL REFERENCES accounts (account_id) ON UPDATE CASCADE ON DELETE RESTRICT NOT DEFERRABLE INITIALLY IMMEDIATE, amount decimal(19,6) NOT NULL, flag varchar(1) NOT NULL CHECK (flag IN ('C','D')) );
注意:transaction_details 表沒有指定明確的借記/貸記賬戶,因為系統應該能夠在單筆交易中藉記/貸記多個賬戶。
此 DDL 創建以下要求:在 transactions_details 表上送出數據庫事務後,它必須為每個 借記和貸記相同的金額
transaction_id
,例如:INSERT INTO accounts VALUES (100, 'Accounts receivable'); INSERT INTO accounts VALUES (200, 'Revenue'); INSERT INTO transactions VALUES (1, CURRENT_DATE); -- The following must succeed BEGIN; INSERT INTO transactions_details VALUES (DEFAULT, 1, 100, '1000'::decimal, 'D'); INSERT INTO transactions_details VALUES (DEFAULT, 1, 200, '1000'::decimal, 'C'); COMMIT; -- But this must raise some error BEGIN; INSERT INTO transactions_details VALUES (DEFAULT, 1, 100, '1000'::decimal, 'D'); INSERT INTO transactions_details VALUES (DEFAULT, 1, 200, '500'::decimal, 'C'); COMMIT;
是否可以在 PostgreSQL 數據庫中實現這一點?無需指定額外的表來儲存觸發器狀態。
首先,這正是我在詢問對子集聚合建模約束時想到的問題?這當然是開始的地方。這個問題比這個更籠統,所以我在這裡的回答將有更多關於實際方法的資訊。
您可能不想在 PostgreSQL 中以聲明方式執行此操作。唯一可能的聲明式解決方案要麼破壞 1NF,要麼非常複雜,因此這意味著必須強制執行。
在LedgerSMB 中,我們希望分兩個階段執行此強制執行(兩個階段都很嚴格)。
- 所有日記帳分錄都將通過儲存過程輸入。這些儲存過程將接受行項目列表作為數組並檢查總和是否等於 0。我們在數據庫中的模型是我們有一個單金額列,其中負數是藉方,正數是貸方(如果我是從頭開始,我會將正數作為藉方,將負數作為貸方,因為這更自然一些,但這裡的原因是模糊的)。借方和貸方在儲存中合併,並在表示層檢索時分離。這使得執行總計更容易。
- 我們將使用延遲約束觸發器,它將根據表上的系統欄位檢查送出。這意味著在給定交易中輸入的行必須平衡,但我們可以在行本身之外做到這一點。