交易、参考以及如何执行复式记账?(PG)

Coc*_*sin 8 postgresql constraint transaction

复式记账是

一组用于在财务会计系统中记录财务信息的规则,其中每个交易或事件至少更改两个不同的名义分类账帐户。

一个账户可以“借”也可以“贷”,所有贷记的总和必须等于所有借记的总和。

你将如何在 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'))
);
Run Code Online (Sandbox Code Playgroud)

注意: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;
Run Code Online (Sandbox Code Playgroud)

是否可以在 PostgreSQL 数据库中实现它?无需指定额外的表来存储触发器状态。

Chr*_*ers 5

首先,这正是我在询问对子集聚合的建模约束时想到的问题这当然是开始的地方。这个问题比这个问题更普遍,所以我在这里的回答将提供更多关于实际方法的信息。

您可能不想在 PostgreSQL 中以声明方式执行此操作。唯一可能的声明式解决方案要么破坏 1NF,要么非常复杂,因此这意味着必须这样做。

LedgerSMB 中,我们希望分两个阶段(两个阶段都是严格的)执行此操作。

  1. 所有日记帐分录都将通过存储过程输入。这些存储过程将接受行项目列表作为数组并检查总和是否等于 0。我们在 db 中的模型是我们有一个单一的金额列,负数是借方,正数是贷方(如果我是从头开始,我会将正数作为借方,将负数作为贷方,因为这更自然一点,但这里的原因并不明确)。借方和贷方在存储时合并,在表示层检索时分开。这使得运行总计更容易。

  2. 我们将使用延迟约束触发器,它将根据表上的系统字段检查提交。这意味着在给定交易中输入的行必须平衡,但我们可以在行本身之外做到这一点。