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 数据库中实现它?无需指定额外的表来存储触发器状态。
首先,这正是我在询问对子集聚合的建模约束时想到的问题?这当然是开始的地方。这个问题比这个问题更普遍,所以我在这里的回答将提供更多关于实际方法的信息。
您可能不想在 PostgreSQL 中以声明方式执行此操作。唯一可能的声明式解决方案要么破坏 1NF,要么非常复杂,因此这意味着必须这样做。
在LedgerSMB 中,我们希望分两个阶段(两个阶段都是严格的)执行此操作。
所有日记帐分录都将通过存储过程输入。这些存储过程将接受行项目列表作为数组并检查总和是否等于 0。我们在 db 中的模型是我们有一个单一的金额列,负数是借方,正数是贷方(如果我是从头开始,我会将正数作为借方,将负数作为贷方,因为这更自然一点,但这里的原因并不明确)。借方和贷方在存储时合并,在表示层检索时分开。这使得运行总计更容易。
我们将使用延迟约束触发器,它将根据表上的系统字段检查提交。这意味着在给定交易中输入的行必须平衡,但我们可以在行本身之外做到这一点。