我使用的是PostgreSQL,但我认为大多数高端数据库必须具有一些类似的功能,而且,针对它们的解决方案可能会为我带来灵感,因此,请不要考虑此特定于PostgreSQL。
我知道我不是第一个尝试解决此问题的人,因此我认为这里值得一问,但我正在尝试评估建模会计数据的成本,以便使每笔交易都达到基本平衡。会计数据是仅追加的。这里的总体约束(用伪代码编写)可能大致类似于:
CREATE TABLE journal_entry (
id bigserial not null unique, --artificial candidate key
journal_type_id int references journal_type(id),
reference text, -- source document identifier, unique per journal
date_posted date not null,
PRIMARY KEY (journal_type_id, reference)
);
CREATE TABLE journal_line (
entry_id bigint references journal_entry(id),
account_id int not null references account(id),
amount numeric not null,
line_id bigserial not null unique,
CHECK ((sum(amount) over (partition by entry_id) = 0) -- this won't work
);
显然,这样的检查约束永远不会起作用。它按行运行,并可能检查整个数据库。因此,它将始终失败,并且执行起来会很缓慢。
所以我的问题是建模此约束的最佳方法是什么?到目前为止,我基本上已经看了两个想法。想知道这是否是唯一的方法,或者是否有人有更好的方法(除了将其置于应用程序级别或存储的过程之外)。
- 我可以借鉴会计界关于原始账簿和最终账簿(一般日记帐与总帐)之间的区别的概念。在这方面,我可以将此模型建模为附加到日记帐分录上的日记帐行,对数组施加约束(以PostgreSQL的术语,从unnest(je.line_items)中选择sum(amount)= 0。将这些保存到订单项表中,在该表中可以更容易地实施各个列约束,而在索引等处可能更有用,这就是我所追求的方向。
- 我可以尝试编写一个约束触发器,该触发器将针对每个事务强制执行此操作,其思路是一系列0的总和始终为0。
我将这些与当前在存储过程中执行逻辑的方法进行权衡。相对于约束条件的数学证明优于单元测试的想法,要权衡复杂性成本。上面#1的主要缺点是,作为元组的类型是PostgreSQL中的那些区域之一,该区域会出现不一致的行为并定期改变假设,因此我什至希望该区域的行为会随着时间而改变。设计将来的安全版本并非易事。
还有其他方法可以解决此问题,这些问题将在每个表中扩展到数百万条记录吗?我想念什么吗?我错过了权衡吗?
为了回应Craig关于以下版本的观点,至少必须在PostgreSQL 9.2及更高版本(也许是9.1及更高版本,但我们可以直接使用9.2)上运行。