トランザクション、参照、および複式簿記を実施する方法は?(PG)


8

複式簿記は

すべてのトランザクションまたはイベントが少なくとも2つの異なる名目元帳勘定を変更する財務会計システムで財務情報を記録するための一連のルール。

アカウントは「借方」または「貸方」にすることができ、すべての貸方の合計は、すべての借方の合計と等しくなければなりません。

これを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データベースに実装することは可能ですか?トリガーの状態を格納する追加のテーブルを指定しない。

回答:


5

最初に、これはサブセット集約のモデリング制約を尋ねたときに私が念頭に置いていた質問とまったく同じですか?それは確かに開始する場所です。その質問はこれよりも一般的です。そのため、ここでの私の答えは、実際的なアプローチについてもう少し情報があります。

あなたはおそらくこれをPostgreSQLで宣言的に実行したくないでしょう。唯一の可能な宣言的ソリューションは1NFを破壊するか、非常に複雑であるため、これは命令的に実行することを意味します。

LedgerSMB我々は二つの段階(どちらもが厳しい)で、この執行を行うことを期待しています。

  1. すべてのジャーナルエントリは、ストアドプロシージャを介して送信されます。これらのストアドプロシージャは、行項目のリストを配列として受け入れ、合計が0に等しいことを確認します。dbのモデルでは、負の数値が借方になり、正の数値が貸方になる単一の金額列があります(最初から、借方として正の数を、貸方として負の数を指定します。これは少し自然なことですが、ここでの理由は不明です)。借方と貸方は、ストレージでマージされ、取得時にプレゼンテーション層で分離されます。これにより、合計が非常に簡単になります。

  2. テーブルのシステムフィールドに基づいてコミットをチェックする遅延制約トリガーを使用します。これは、特定のトランザクションで入力された行はバランスを取る必要があることを意味しますが、これは行自体を超えて行うことができます。


ところで、多くの複式簿記をしているなら、eは来年かそこら以内に(postgreSQLで)私たちの財務スキーマを再設計することを期待しています。オープンソースプロジェクトとのコラボレーションに興味があるかどうかはわかりませんが、招待状を延ばすと思いました。
Chris Travers 2013年

パーティーに遅れましたが、「テーブルのシステムフィールドに基づいてコミットを確認しますか」と説明してもらえますか?挿入された行を見つけるためにシステムフィールドxminを使用していますか?私はこの正確な状況に直面しており、これが解決策に近づいている唯一のスレッドです。しかし、私は無知です。
Code Poet、

うん。トランザクションによって作成された行を見て、それらの行の合計が0であることを主張できます。つまり、基本的に、xminなどのシステム列をチェックします。
Chris Travers

4

別のアプローチは、単一のレコードを構成するのは金融金額の振替であるという立場を採用することです。

したがって、次のような構造になります。

create table ... (
  id                integer,
  debit_account_id  not null REFERENCES accounts (account_id),
  credit_account_id not null REFERENCES accounts (account_id),
  amount            numeric not null);

チェック制約により、借方勘定と貸方勘定が異なり、保管する金額が1つだけであることを確認できます。したがって、保証された整合性があり、これはデータモデルが自然に提供すべきものです。

私はこのアプローチをうまく採用したシステムで働いてきました。特定のアカウントに対するレコードのクエリの効率は少し低くなりますが、テーブルはよりコンパクトになり、借方のみまたはクレジットのみの方が少し効率的だったため、アカウントのクエリがより効率的になりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.