交易,参考资料以及如何执行双重记账?(PG)


8

两次入境簿记是

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

帐户可以是“借方”或“贷方”,所有贷方的总和必须等于所有借方的总和。

您将如何在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数据库中实现呢?不指定其他表来存储触发状态。

Answers:


5

首先,当我问对子集的建模约束时,这正是我想到的问题吗?这当然是起点。这个问题比这个问题更笼统,因此我在这里的答案将提供有关实用方法的更多信息。

您可能不想在PostgreSQL中声明式地执行此操作。唯一可能的声明式解决方案要么破坏1NF,要么极其复杂,因此这势在必行。

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

  1. 所有日记帐分录将通过存储过程进入。这些存储过程将接受行项目列表作为数组,并检查总和是否等于0。我们在db中的模型是,我们有一个金额列,其中负数是借方,正数是贷方(如果我是从头开始,我将借贷记为正数,将贷记记为负数,因为这自然而然,但此处的原因不清楚。借方和贷方在存储中合并,在表示层取回时分开。这使运行总计变得容易得多。

  2. 我们将使用延迟约束触发器,该触发器将根据表上的系统字段检查提交。这意味着在给定事务中输入的行必须保持平衡,但是我们可以在行本身之外进行此操作。


顺便说一句,如果您要进行多次重复记帐,e希望在明年左右重新设计我们的财务模式(基于postgreSQL)。我不知道您是否会对与开源项目合作感兴趣,但认为我会发出邀请。
克里斯·特拉弗斯

我迟到了,但是请您解释一下-“将根据表上的系统字段检查提交”吗?您是否正在使用系统字段xmin找出要插入的行?我面临着这种确切的情况,这是唯一接近解决方案的线程。但是,我一无所知。
代码诗人

是的 我们可以查看事务创建的行,并坚持认为其中的金额之和为0。这基本上意味着检查xmin之类的系统列。
克里斯·特拉弗斯

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);

支票约束可以确保借方帐户和贷方帐户不同,并且只能存储一个金额。因此,保证了完整性,这是数据模型应自然提供的。

我曾与成功采用这种方法的系统一起工作过。针对特定帐户查询任何记录的效率要低一些,但是表更加紧凑,仅作为借项或仅作为贷项查询一个帐户的效率要高一些。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.