双重记账模式

为个人使用和帮助管理一个非常小的企业建立一个双重记账系统。试图加入一些现在看起来相关的功能。

业务规则

对于不熟悉会计的人来说,逻辑是:金钱既不会被创造也不会被销毁,只会从一个账户转移到另一个账户。每笔交易都有借方和贷方。以下是一些例子:

  1. 来自雇主的工资: 贷方:工资,借方:银行账户 - 钱来自你的工资,进入了你的银行账户。

  2. 支付房租: 贷方:银行账户,借方:房租 - 钱来自你的银行账户,进入了你的房租账户。

账户可以是“存量”账户,即账户余额是累积的(银行账户是一个很好的例子),也可以是“流量”账户,即账户余额是非累积的(房租是一个很好的例子)。

设计背后的逻辑

这个想法是有一个主要的JournalDB表来存储主要条目。表JournalTx存储每个参与交易的账户。每个来自JournalDB的条目都有一个ID,而每个来自JournalTx的交易都与一个日志条目相关联。基本情况是JournalDB上有1个条目,而JournalTx中有两个(或更多)交易。每个条目可以有一个cost_center,一个project和一些其他属性。

根据这个问题,基本上有两种设计方式 - 一行对应一次交易的方式和两行对应一次交易的方式。在第一种方式中,我会有一行包含借方账户和贷方账户,在第二种方式中(即这种方式),会有n行,每行表示受影响的每个账户。

账户

账户表是会计术语中的科目表。它具有层次结构 - 我使用了邻接列表样式。虽然不是很频繁,但账户将进行CRUD操作。我添加了parent_imediateparent_second作为一个非常丑陋的解决方案来进行聚合(例如计算资产账户的总额),但考虑到挑战(在长时间的研究后也不知道如何做到这一点),这似乎是一个简单的解决办法 - 对此问题的任何输入或建议也都受欢迎。

主要查询

获取报告,通常是每月一次:基本上是所有受影响的交易的汇总,以及每个帐户。最理想的情况是一个数据透视表(列为日期),每行是一个帐户。我想一个“堆叠”版本也可以很好地工作。

账户只是一个维度 - 例如,我可能想按cost_centerproject进行查询。

其他功能

我想要有预算账户的能力(因此有了预算表),同时还想要设定“目标”(我想要度假,花费1000美元)。 我还想要有标签,并且能够设置循环账单(即“预期”交易)。

基本关系

一个条目(journal_db)有多个交易(journal_tx)。 一个成本中心、项目等有多个条目。 一个账户有多个交易。 一个联系人有多个条目。

enter image description here

我的主要疑问

我刚开始学习数据库/编程,所以请原谅我可能犯的明显错误。

  1. 从编程/性能/功能的角度来看,这个设计是否稳固?
  2. 如何实现报表功能?是通过查询数据库(派生表)还是创建新表(比如journal_reports),并为每个条目创建触发器来更新账户余额?(在这个问题中看到这不是一个好主意)
  3. 有什么可能我遗漏的内容吗?

谢谢。目前,性能可能是我最后关心的问题。它不应该太大,可能大约有15-20k行的最大表...我应该查询几乎所有的表,大多数查询应该将2个或更多个表连接在一起,但我还没有编写任何查询.. - lowercase00
2个回答

我觉得你的问题比较广泛,很难一下子回答完整。但是关于你的设计,我注意到了一些问题:

总体设计

你的设计中有一些表违反了第一范式。一个很好的例子是contact_adress表,它有adress1adress2adress3这几列。通常一个地点只有一个街道地址,所以只需要一个地址列就足够了。但是如果你真的想要在一个地点添加多个街道地址的能力,你应该将这些地址移到另一个表中(contact_adressstreet_adress)。对于journal_bills表(detail1detail2)和accounts表也是同样的情况:不需要parent_imediateparent_secondparent_third这几个属性,只需要一个parent属性就足够了。要获取第二个或第三个父级,可以使用递归CTE

我不知道你的所有业务需求,但你应该检查一下你的设计是否允许输入荒谬的数据:一个账户既可以是现金又可以是信用吗?如果有货币,汇率是多少?邮政编码可以以一个或多个零开头吗?电话号码呢?对于某些事情,有最佳实践,例如如何处理重复事件。 命名 一些列名很难理解(agaccpmt),如果其他人需要与你的数据库一起工作,这可能会导致可维护性问题。如果你不确定如何命名,请使用样式指南。通常,坚持一致的命名方案是个好主意,例如给每个表一个复数名字。 如何实现报告? 我建议使用简单的SQL查询,并尽量避免使用复杂的函数,如触发器,除非你真的需要,而对于一个简单的应用程序来说,这可能永远都不是必要的。

非常感谢您的建议。关于零件:我决定进行拆分,以便能够包含街道名称、门牌号码、公寓号码等等,但是这些名称确实让人感到困惑,你说得对。我会尽力改进设计,使其更加详细。至于业务需求,我正在代码中进行验证,因为有些验证似乎在数据库层面上实现起来过于复杂。递归CTE似乎是一个很好的选择,我会进一步研究它,看看是否可以实施。至于命名 - 你说得对,我应该让它更易读,让第三方也能理解,我会努力改进。 - lowercase00


An error occurred:

marked(): input parameter is undefined or null
Please report this to https://github.com/markedjs/marked.
- undefined

An error occurred:

marked(): input parameter is undefined or null
Please report this to https://github.com/markedjs/marked.
- undefined

An error occurred:

marked(): input parameter is undefined or null
Please report this to https://github.com/markedjs/marked.
- undefined

An error occurred:

marked(): input parameter is undefined or null
Please report this to https://github.com/markedjs/marked.
- undefined