复式记账的关系数据模型

Relational Data Model for Double-Entry Accounting

假设有一家银行、一家大型商店等希望正确完成内部账户的会计核算,并跟踪客户账户。而不是实现满足当前简单和狭窄需求的东西,这将是 'home brew':这些结果只是当前简单需求的临时拐杖,并且在新需求出现时很难或不可能扩展。

据我了解,Double-Entry Accounting 是一种行之有效的方法,可满足所有会计和审计要求,包括那些在当前时刻。如果实施,它将:

我研究了另一个问题的答案:,它为内部帐户提供了很好的信息。需要一个数据模型,以便可以理解实体;他们的互动;他们的关系,@PerformanceDBA 已经给出了。该模型取自那个答案:

虽然这对于简单的内部账户来说已经足够了,但我需要看到一个提供完整的复式记账方法的数据模型。

需要添加的文章有Journal;内部与外部 Transactions;等..

理想情况下,我想看看这些双录入行在数据库中是什么样子的,整个过程在 SQL 中是什么样子的,每种情况下哪些实体受到影响,等等。案例如下:

  1. 一位客户将现金存入他的账户
  2. 银行每月向所有客户账户收取一次费用(示例批处理作业),
  3. 客户在柜台操作,银行收取手续费(取款+取款手续费),
  4. 玛丽从她的账户中汇款到约翰在同一家银行的账户

我们就叫它System而不是Bank吧,Bank可能模型太复杂了,问题就 以账户和资产运作的系统。客户与系统执行一组操作(存款、取款、后一手续费、批量手续费),以及彼此之间(转账)。

一个。初步

你的方法

首先,我必须赞扬你的态度。很少有人不仅在扎实的基础上思考和工作,而且希望理解和实施 Double-Entry 会计系统,而不是:

  • 要么实施DEA,因此遭受多重re-writes,并且在每个增量、每个新需求中都痛苦,

  • 或实施 DEA,但 re-inventing 从头开始​​,自己弄清楚,并在每次暴露错误时遭受痛苦,并要求修复错误,一个序列永远不会结束。

避免所有这些,并寻求标准方法,受到高度赞扬。

此外,您希望以关系数据模型的形式,您不被日期所奴役;达尔文;费金;等人认为规定了基于 Record ID 的记录归档系统,这削弱了建模练习和由此产生的“数据库”。现在,有些人沉迷于原始的 RFS 并打压 Dr E F Codd 的 Relational Model.

1。答案的方法

如果您不介意,我将从头开始按逻辑顺序解释,这样我就可以避免重复,而不仅仅是回答您的特定要求。如果您完全了解这些要点,我深表歉意。

障碍物

Ideally I would like to see what those double entry rows look like in database terms

这是建模或定义任何事物所需的正确方法的障碍。

  • 以同样的方式在每个文件上标记一个 ID 字段,并使其成为“关键”,削弱了建模练习,因为它阻止了数据分析(数据代表什么)实际上是),在开始时期望 Credit/Debit 对有两行会削弱对事物是什么的理解;会计行为是什么;这些行为有什么影响;最重要的是,数据将如何建模。尤其是在学习的时候。

Aristotle teaches us that:

the least initial deviation from the truth is multiplied later a thousandfold ... a principle is great, rather in power, than in extent; hence that which was small [mistake] at the start turns out a giant [mistake] at the end.

换句话说,开头的一个小错误(例如原则;定义)最后变成了一个大错误。

因此,智力要求首先是要清楚您对建模练习结束时的想法。当然,在学习会计术语时,这也是必需的。

2。答案范围

Assume there is a bank, a large shop, etc, that wants the accounting to be done correctly, for both internal accounts, and keeping track of customer accounts.
Let's just call it System instead of Bank, Bank may be too complex to model ...
Customers perform a set of operations with system (deposits, withdrawals, fee for latter, batch fees), and with each other (transfer).

为了清楚起见,我确定的范围如下。不对请指正:

  • 不是只有总帐,没有客户帐户的小型企业
  • 但是一家小型社区银行,没有分行(总行是分行)
  • 您需要 内部 帐户,其中包括:
  • 一个简单的通用分类帐,
  • 以及外部 帐户,每个客户一个
  • 我想到的最佳概念是一家小型社区银行,或者像一家银行一样运作的企业。一个农业合作社,每个农民都有一个 账户 用于购买,并按月计费和付款,合作社像一家小银行一样运作,拥有完整的一般 Ledger,并提供一些简单的银行服务。
  • 单个赌场(而非连锁赌场)具有相同的要求。
  • 不是拥有多个分支机构的大型银行;各种金融产品;等等
  • 而不是 SystemBank,我将其称为 House。其相关性稍后会清楚。

任何寻求 Double-Entry 方法的人 just Ledger, without外部客户 帐户 ,可以从这个答案中轻松收集。

同理,这里给出的数据模型容易扩展,Ledger可以比给出的简单的大


乙。解决方案

1。 Double-Entry会计

1.1。概念

通过名称知道它是什么;它具有巨大的价值;它比 roll-your-own 系统更好是一回事,了解它的深度足以实施它是另一回事。

  1. 首先,需要对总分类帐和一般会计原则有一定的了解。

  2. 其次,理解金钱代表价值的概念。价值无法被创造或毁灭,它只能被移动。 账户中的一个桶另一个桶,也称为借方(from-account)和贷方(to-account).

  3. 虽然SUM(所有贷方)=SUM(所有借方)是真实的,并且可以从 DEA 系统获得这样的报告,这不是实施所需的理解,这只是一个最终结果.还有更多。

  • 虽然确实 每笔交易都由一对组成:相同金额的一笔贷记和一笔借记,但还有更多。

  • 一对的每条腿;贷方和借方不在同一个账户或分类账中,它们在不同的账户或分类账中,或者 Accounts-and-Ledgers.

  • SUM(all Credits)并不简单,因为它们在那些不同的地方(集合)。它们不在同一 table 的两行中(稍后可能会更多)。同样,SUM(所有借方)

  • 因此两个SUM()分别涵盖了完全不同的集合(Relational Sets),必须先得到,然后才能比较两个SUM()

1.2。了解 Double-Entry 会计

在尝试 DEA 实施之前,我们需要正确理解我们正在实施的东西。我建议如下:

  1. 你说得对,第一个原则就是要站在Credit/Debit对的角度,处理任何账簿,总账;客户账户;银行账户;等等
  • 这是要坚持的首要心态,与需要在这个或那个账户或分类账中完成的任何事情分开。

  • 我把它放在了最上面;左边,在数据模型中,这样所有文章的从属关系都以视觉方式呈现。

  1. 目的或Double-Entry会计系统的目标是:
  • 消除(不只是减少)所谓的:

    • “丢”钱

    • “丢失”交易(Credit/Debit 对的一方或另一方)

    • 追下去浪费时间

    • 不仅可以轻松找到钱,而且可以快速确定它到底发生了什么,现在在哪里。

  • 完整的审计功能
    仅仅保持良好的账目是不够的,对于一个为别人的钱做账的企业来说,当务之急是audit-able。也就是说,任何会计师或审计师都必须能够不受阻碍地检查账簿。

    • 这就是为什么第一件事是局外人,例如。审计师想知道的是,SUM(所有贷方)= SUM(所有借方)。这也解释了为什么 DEA 概念高于公司可能保留的任何账户或会计系统。
  • 虽然是第三级的,但最大的好处是可以轻松快速地完成日常或月末任务,例如试算表或结账。所有报告;报表;资产负债表;等,可以简单地获得(如果数据库是关系,则使用单个 SELECT)。

  1. 然后 准备 Double-Entry Bookkeeping.
  2. 的维基百科条目
  • 网络上有很多误导性的信息,维基百科尤其糟糕,它永远在变化(真理不会改变,虚假会随着天气而变化),但是对不起,我们只有这些。尽管它很长,但仅用于获得概述,没有结构或逻辑描述。按照 link 获取更多信息。

  • 我不完全同意Wikipedia article中的术语。尽管如此,为了避免可避免的混淆,我将使用这些术语。

  • 网上有教程,有些教程比其他教程好。建议任何正在实施适当会计系统的人使用或不使用 DEA。这需要时间,它与这样的答案无关,这就是为什么我 linked Wikipedia article.

2。商业交易

Ideally I would like to see what those double entry rows looks like in database terms, what the whole process will look like in SQL, which entities are affected in each case, etc.

好的。让我们先了解事务,然后了解支持它们的数据模型,然后检查示例行。任何其他顺序将是 counter-productive,并导致不必要的 back-and-forth.

你的编号。绿色是一般 Ledger 中的 House,蓝色是外部客户 Account,黑色是中性的。

  • 这是Treatment的第一个增量,一个事物是如何被对待的,在不同的场景下(你的关注点,你要求的具体例子,正是正确)。

  • Credit/Debit对
    这就是DEA的第一原则,理解pair,为pair,无非pair。

不要担心常规 LedgerAccount 是如何设置的,或者数据模型是什么样的。从会计师的角度思考(书本上必须做的事情),而不是从会计师的角度思考eloper(必须在系统中完成的操作)。

请注意,每对的每条腿都在一组 (Ledger) 中,或分为两组 (一条腿在 Ledger 中,另一条腿在 Account).没有双腿在 Account.

中的对
  • 由于实施了 DEA,每个 业务交易 (与数据库交易不同)由两个操作组成,每个 Credit/Debit 分支一个。这两个动作是paper-based账簿中的两个条目。
  1. A Client deposits cash to his account

  • 在 DayEnd 过程中,除其他任务外,所有现金都会被核算和检查。这一天是关闭的。 HouseCash 中超出银行认为日常现金交易所需的所有现金,将转移到 HouseReserve

  1. The Bank charges fees once a month to all Clients accounts (sample batch job)

  • Account 收费 Fee
  • Fee 依赖于 AccountType_Ext
  • 这是最简单的情况。如果 Fee 依赖于其他东西,例如 Account 中的事务数;或者 CurrentBalance 低于或高于某个限制;等,未显示。我相信你能弄明白。
  1. A Client does some operation over the counter, and the Bank charges a fee (cash withdrawal + withdrawal fee),
  • 简单交易不产生手续费,Deposit/Withdrawal已经给出。让我们检查一个实际收取费用的业务交易。

  • Mary 给儿子 Fred 寄了 500 美元,他正在海外旅行寻找鲸鱼来拯救,运行 没钱了。银行对海外银行转账收取 30 美元。 Fred 可以在任何合作银行分行收取资金(以等值 500 美元的当地货币计算)。
  • 要真正将钱转到外国银行,House 必须与提供国际结算和货币兑换服务的当地大银行互动。这与我们无关,也没有显示。在任何情况下,所有这些类型的 Interbank 交易都是每天批处理和处理一次,而不是每个 AccountTransaction.
  • 一次。
  • 在这个简单的 DEA 系统中,HouseLedger 中没有货币账户。这很容易实现。
  1. Mary sends some money from her account, to John's account, which is in the same bank

  • 这笔钱目前在 Mary 的账户中(今天前一天存入),这就是它在 HouseReserve 而不是 HouseCash
  • 的原因
  • 钱从 HouseReserve 转到 HouseCash 因为约翰今天可能会去银行取钱。
  • 如上面示例 [1.3] 中所述,在 DayEnd 过程中,所有 AccountsHouseCash 中的任何资金都将移至 HouseReserve。未显示。

3。关系数据模型 • 初始

现在让我们看看数据建模师做了什么来支持会计的需求,即业务交易。

  • 这当然是 处理 的第二个增量,建模者已经理解了现实世界的业务交易,用关系术语 (FOPC) ; RM; 逻辑; 标准化)

  • 这不是满足重述范围所需的最简单的数据模型。

  • 有更简单的模型(稍后会详细介绍),但它们存在这个模型没有的问题,这些问题即使不是必须的,也是需要避免的。

  • 图像太大 in-line 无法查看。在新标签页中打开图片,以全尺寸欣赏。

3.1。符号

  • 我所有的数据模型都在 IDEF1X 中呈现,这是自 1993 年以来的关系数据库建模标准。

  • 我的 IDEF1X Introduction关系模型 新手的必读之物,或者其建模方法。请注意,IDEF1X 模型具有丰富的细节和精度,显示了 所有 所需的细节,而 home-grown 模型由于不了解标准的要求,因此定义少得多。这意味着,符号需要被完全理解。

3.2。内容

  • 别人制作的真正的关系数据模型和我的主要区别是:
    一个 业务交易(总是两个动作;两条腿,每个 Credit/Debit 一个)受到具有两侧的单行的影响,每个 Credit/Debit,
    AccountTransactionLedgerTransaction.

  • 大多数建模者会为 Credit/Debit 对建模两行,每条腿或每条边(嘿,一条腿是信用,另一条腿是借方,如果我将其标准化,我得到两行).

  • 错了。如果我告诉你 Fred 是 Sally 的父亲,你知道,从那个事实来看,Sally 是 Fred 的女儿。

  • 一个FOREIGN KEY只需要声明一次,而不是每边一次。

  • 同样,[=412=Credit/Debit 对是单个业务交易,
    单个 Atomic 文章,可以从两面看到,就像一枚硬币的两面。如此建模。

  • 所有形式的 preventable 错误都被阻止,搜索“丢失”的腿被消除。

  • 即使是那些sub-standard OLTP 代码,这会导致相当多的table 并发问题,如果实现这种方法,这是一篇不会出现这些问题的文章.

  • 此外,%Transactiontable中的行数减半。

  • 我整理了文章,
    外部Account
    内部LedgerLedgerTransaction
    Internal-ExternalAccountTransaction
    很清楚。

  • 连同来自 Wikipedia entry.

    的定义块
  • 熟悉 DEA Credit/Debit 对后,现在研究 Treatment 对。请注意,处理是不同的,它基于许多标准(三种帐户类型;六种 Ledger 类型;等),而这又基于总帐的复杂性。

  • Ledger 很简单,只有 Asset/Liability 个帐户。当然,你可以自由扩展它。

  • eagle-eyed 会注意到 AccountStatement.ClosingBalanceLedgerStatement.ClosingBalance 实际上可以导出,因此(从表面上看)不应存储。但是,这些是已发布的数字,例如。每个账户的月度银行对账单,因此需要审计,因此必须存储。

对于该问题的完整处理,包括注意事项;定义;治疗,参考这个问答:

3.3。摘要

结束本节,我们应该达成这样的理解:

  • DEA的总体原则,Credit/Debit对,纯知识

  • 典型的商业交易,总是一对Credit/Debit,两条腿,两个记入账簿

  • 更深入地了解上述交易的处理方式

  • House(小银行;合作社;赌场)管理的环境(内部Ledger和外部客户Account

  • 初步了解建议处理所有这些问题的数据模型。


4。关系数据模型 • 完整

又来了,有全套样本数据。

  • 关于主键:

  • 请注意 LedgerNoAccountNo 不是代理人,它们对组织有意义,在 Ledger 的排序和结构等方面。它们是 s table 数字,而不是 AUTOINCREMENTIDENTITY 或任何类似的数字。

  • LedgerTransactionAccountTransaction 的主键是纯复合关系键。

  • 它不是 paper-based 会计师所钟爱的某种交易编号。

  • 也不是残废Record ID

  • 备用键对人类更有意义,因此我在示例中使用了它们(业务交易,上面 [2],下面 [5 ]).这个答案已经分层,试图将数百个 1's, 2's3’s 相互联系起来将是一场噩梦。

  • 如果我们想理解某物的含义,我们需要抓住事物存在的意义,而不是通过给它一个数字来删减意义。

  • 在示例数据中,主键是粗体。


5。与行的业务交易

Ideally I would like to see what those double entry rows looks like in database terms, what the whole process will look like in SQL, which entities are affected in each case, etc.

现在我们了解了业务事务和满足需求的数据模型,我们可以检查业务事务 以及 受影响的行。

  • 每个业务交易,在 DEA 术语中,有两条腿,paper-based 账簿中的两个条目,对于 Credit/Debit 对中的每个,
    还是一个单一的业务交易,现在:
    对于 Credit/Debit 对中的每一对,它受到 单行 的影响。

  • 这是理解的第三个增量处理:业务交易;实现它们的数据模型;现在,受影响的行

  • 示例数据库行以 table 名称作为前缀。
    加号表示 INSERT
    减号表示 DELETE
    等于表示 UPDATE.

  1. A Client deposits cash to his account

  1. The Bank charges fees once a month to all Clients accounts (sample batch job)

  • 这也是一个批处理作业,只是 MonthEnd 过程中的一个任务。
  • 注意事项e 日期是该月的第一天。
  1. A Client does some operation over the counter, and the Bank charges a fee (cash withdrawal + withdrawal fee),

  • 说清楚,就是三个业务交易;每个条目两个,Credit/Debit 对的每一侧一个;每个受一个数据库行的影响。
  1. Mary sends some money from her account, to John's account, which is in the same bank


6. SQL代码

给猫剥皮的方法通常有几种(代码),但是如果猫是活的就很少了(高并发系统的代码)。

  • 关系模型 建立 一阶谓词演算(又名一阶逻辑),所有定义( DDL),因此所有查询 (DML) 都是完全符合逻辑的。

  • 因此,符合这种理解的数据模型是完全符合逻辑的。

  • 针对此类数据模型的查询非常简单:逻辑和 straight-forward。他们有 none 基于 Record ID 的归档系统所需的复杂代码。

因此,在 SQL 代码请求可能的几种方法中,我给出了最直接和合乎逻辑的方法。

The code examples are that which is appropriate for SO, it is imperative that you trap and recover from errors; that you do not attempt anything that will fail (check the validity of the action before using a verb), and follow OLTP Standards for ACID Transactions, etc. The example code given here are the relevant snippets only.

6.1。 SQL 查看 • 账户当前余额

由于这个代码段在很多地方都用到了,所以让我们做正确的事并创建一个视图。

  • 请注意,在正版 SQL 平台上,源代码是经过编译的,并且 运行 在提交时,存储过程和视图以编译后的形式存储,因此消除了在每次执行时编译。不像米老鼠 NONsql 套件。

  • High-end 商业 SQL 平台做的更多,例如缓存视图的查询计划,以及存储过程中的查询。

CREATE VIEW Account_Current_V
    AS 
SELECT  AccountNo, 
    Date = DATEADD( DD, -1, GETDATE() ),     -- show /as of/ previous day 
    ASS.ClosingBalance,                      -- 1st of this month
    TotalCredit = ( 
        SELECT SUM( Amount ) 
            FROM AccountTransaction  ATT
            WHERE ATT.AccountNo = ASS.AccountNo 
                AND XactTypeCode_Ext IN ( "AC", "Dp" ) 
                -- >= 1st day of this month yy.mm.01  /AND <= current date/
                AND DateTime >= CONVERT( CHAR(6), GETDATE(), 2 ) + "01" 
            ), 
    TotalDebit = ( 
        SELECT SUM( Amount ) 
            FROM AccountTransaction ATT
            WHERE ATT.AccountNo = ASS.AccountNo 
                AND XactTypeCode_Ext NOT IN ( "AC", "Dp" ) 
                AND DateTime >= CONVERT( CHAR(6), GETDATE(), 2 ) + "01" 
                ),
    CurrentBalance = ClosingBalance + 
        <TotalCredit> -   -- subquery above 
        <TotalDebit>      -- subquery above 
    FROM AccountStatement  ASS
                                             -- 1st day of this month
    WHERE ASS.Date = CONVERT( CHAR(6), GETDATE(), 2 ) + "01"

6.2。 SQL 交易• [1.2] 从[外部] 账户提款

另一个 DEA 业务交易的过程。

CREATE PROC Account_Withdraw_tr ( 
    @AccountNo, 
    @Amount
    ) AS
    IF EXISTS ( SELECT 1                       -- validate before verb
            FROM AccountCurrent_V 
            WHERE AccountNo = @AccountNo 
                AND CurrentBalance >= @Amount  -- withdrawal is possible
            )
        BEGIN
        SELECT @LedgerNo = LedgerNo 
            FROM Ledger 
            WHERE Name = "HouseCash"
        BEGIN TRAN
        INSERT AccountTransaction 
            VALUES ( @LedgerNo, GETDATE(), "Cr", "Wd", @AccountNo, @Amount )
        COMMIT TRAN
        END

6.3。 SQL交易•[1.1]存款到[外部]账户

一个 proc,设置为 SQL 事务,用于执行 DEA 业务事务。

CREATE PROC Account_Deposit_tr ( 
    @AccountNo, 
    @Amount
    ) AS
    ... IF EXISTS, etc ...                   -- validate before verb
        BEGIN
        SELECT @LedgerNo ...
        BEGIN TRAN
        INSERT AccountTransaction 
            VALUES ( @LedgerNo, GETDATE(), "Dr", "Dp", @AccountNo, @Amount )
        COMMIT TRAN
        END

6.4。 SQL 交易 • [内部] 分类帐账户转账

将任何业务交易添加到 LedgerAccount 的过程。它总是:

  • 一个LedgerTransaction.LedgerNo,这是Credit支线
  • 一个 LedgerTransaction.LedgerNo_Dr,这是 Debit 支线。
  • 来电者给的
CREATE PROC Ledger_Xact_tr ( 
    @LedgerNo,    -- Credit Ledger Account
    @LedgerNo_Dr, -- Debit  Ledger Account
    @Amount 
    ) AS
    ... IF EXISTS, etc ...
        BEGIN
        SELECT @LedgerNo ...
        BEGIN TRAN
        INSERT LedgerTransaction  
            VALUES ( @LedgerNo, GETDATE(), @LedgerNo_Dr, @Amount )
        COMMIT TRAN
        END

6.5。 SQL 批处理任务 • 账户月末

这使用类似于 [6.1 帐户当前余额] 的视图,适用于任何月份(视图是通用的),值仅限于月份。来电者选择上个月。

  • 此答案现在超出了 SO 限制,因此它在 link Account_Month_V.
  • 中提供

只有一个任务,在存储过程中,处理 AccountStatement 的月末,它作为批处理作业执行。同样,只是必要的代码,需要添加基础设施。

CREATE PROC Account_MonthEnd_btr ( ... )
    AS    
... begin loop
... batch transaction control (eg. 500 rows per xact), etc ...
INSERT AccountStatement
    SELECT  ACT.AccountNo,
            CONVERT( CHAR(6), GETDATE(), 2 ) + "01",  -- 1st day THIS month
            AMV.ClosingBalance,                       -- for PREVIOUS month
            AMV.TotalCredit,
            AMV.TotalDebit
        FROM Account ACT 
            JOIN Account_Month_V AMV               -- follow link for code
                ON ACT.AccountNo = AMV.AccountNo
                                                   -- 1st day PREVIOUS month
        WHERE AMV.OpeningDate = DATEADD( MM, -1, ACT.Date ) 
... end loop
... batch transaction control, etc ...

6.6。 SQL 报表 • SUM(贷方)与 SUM(借方)

While it is true that the SUM( all Credits ) = SUM( all Debits ), and one can obtain such a report from a DEA system, that is not the understanding. There is more to it.

希望我已经给出了方法和细节,并涵盖了理解更多,这样您现在就可以编写需要 SELECT 才能轻松生成所需的报告。

或者可能是外部 Accounts 的月结单,总计 运行 列 AccountBalance。思考:银行对帐单。

  • 真正的关系数据库的众多高效之一是,任何报告都可以通过 单个 SELECT 命令.
  • 提供服务

一个 PDF

最后但同样重要的是,希望拥有数据模型;示例交易;代码片段全部组织在一个 PDF 中,A3(我的美国朋友是 11x17)。为了学习和注释,打印成A2 (17x22)。