Preface
有一个客观事实:审计要求。此外,在处理公共资金时,必须遵守立法机关的规定。
您不必实现完整的会计要求,您可以仅实现您需要的部分。
相反,实施某些事情是不明智的以外标准会计要求(其中的一部分),因为这保证了当错误数量或负载超过某个阈值或系统扩展时,您将不得不重新实现。这种成本是可以而且应该避免的。
还需要说明的是:不要聘请不合格、未经认可的“审核员”。这将会产生后果,就像你雇佣了一个不合格的开发人员一样。如果税务局对您罚款,情况可能会更糟。
Method
不太原始国家的标准会计方法是这样的。如果你愿意的话,在其他方面也是“最佳实践”。
该方法适用于任何有类似操作的系统;需要;历史月度数据与当月需求的比较,例如库存控制等。
考虑
首先,考虑因素。
-
切勿重复数据。
If the Current Balance
可以导出(正如您所注意到的,这里很简单),不要将其与摘要列重复。
- 这样的列是数据的重复。它违反了规范化规则。
- 此外,它creates an
Update Anomaly
,否则不存在。
-
如果您确实使用摘要列,则当新AccountTransaction
插入后,汇总栏Current Balance
value已过时,因此必须始终更新。这就是结果Update Anomaly
。这消除了拥有它的价值。
-
外部出版物。
单独点。如果余额被公布,如每月的银行对账单,此类文件通常具有法律限制和影响,因此公布的当前余额值在公布后不得改变。
-
为了纠正一个错误AccountTransaction
过去所做的,现在正在纠正的,必要的纠正或调整,被作为新的AccountTransaction
当月(即使它适用于前一个月或持续时间)。
-
这是因为适用月份已关闭;已审核;并发表,因为历史一旦发生并被记录下来,就无法改变。唯一的有效的月份是当前月份。
-
对于计息系统等,在不那么原始的国家,当发现错误时,它会产生历史性影响(例如,您在 2015 年 4 月发现证券每月计算的利息不正确,因为2014 年 12 月),今天根据错误天数计算更正利息支付/扣除的值,并将总和插入为AccountTransaction
当月。同样,唯一有效的月份是当前月份。
当然,证券的利率也必须得到纠正,这样错误就不会重复。
-
同样的原则也适用于库存控制系统。它保持理智。
-
所有真实的会计系统(即那些由适用国家/地区审计机构认可的系统,而不是比比皆是的米老鼠“包”)都使用复式记账会计所有人的系统AccountTransactions
,正是因为它可以防止大量错误,其中最重要的是,资金不会“丢失”。这需要总账和复式记账会计。
- 你没有要求那个,你不需要那个,所以我不在这里描述它。但请记住这一点,以防钱“失踪”,因为这是你必须实施的,而不是一些创可贴解决方案;不是另一个未经认可的“一揽子计划”。
这个答案服务于所提出的问题,即not复式记账会计。
有关该主题的完整处理(详细数据模型、会计事务示例、受影响的行以及 SQL 代码示例),请参阅此问答:
复式记账会计的关系数据模型 https://stackoverflow.com/a/59465148/484814.
- The major issues that affect performance are outside the scope of this question, but to furnish a short and determinant answer: it is dependent on:
-
无论您是否实现真正的关系数据库(例如 1960 年代的记录归档系统,其特点是Record IDs
,为了方便起见部署在 SQL 容器中)。
-
无论您使用的是真正的 SQL 平台(结构化、稳定、可靠、符合 SQL 标准、OLTP 等)还是假冒的 SQL 免费软件(大量程序、不断变化、没有抱怨、像鱼一样缩放)。
-
无论表的数量如何,使用真正的关系键等都将保持高性能。
-
相反,RFS 的性能会很差,他们根本无法执行。 “规模”在 RFS 上下文中使用时是一个欺诈性术语:它隐藏原因并寻求解决除原因之外的所有问题。最重要的是,这样的系统没有关系完整性;关系力;或关系 DBMS 的关系速度。
执行
关系数据模型 • 银行账户
关系数据模型 • 库存
Notation
-
我所有的数据模型都呈现在IDEF1X https://www.idef.com/idef1x-data-modeling-method/,自 1993 年以来的关系数据库建模标准。
-
My IDEF1X简介 https://www.softwaregems.com.au/Documents/Documentary%20Examples/IDEF1X%20Introduction.pdf对于刚接触该领域的人来说是必读的关系模型,或其建模方法。请注意,IDEF1X 模型具有丰富的细节和精度,显示了所有所需的细节,而国产模型的细节却远远不够。这意味着,必须理解该符号。
Content
-
对于每个AccountNo
,将会有一个AccountStatement
每月行,其中ClosingBalance
;陈述Date
(通常是下个月的第一天)以及关闭月份的其他报表详细信息。
-
这不是存储的“重复”值或可导出值,因为 (a) 该值仅适用于一个Date
,(b) 出于审计和健全目的而需要,并且 (c) 提供显着的性能优势(消除 SUM(所有交易))。
对于库存,对于每个PartCode
,将会有一个PartAudit
每月行,有QtyOnHand
column.
-
它还有一个附加值,因为它限制了需要查询的事务行的范围to当月
-
同样,如果您的表是关系型表并且您有 SQL 平台,则主键AccountTransaction
将 (AccountNo
, 交易DateTime
)它将以毫秒的速度检索交易。
-
而对于记录归档系统,“主键”将是AccountTransactionID
,您将按交易日期检索当前月份,该日期可能会也可能不会正确索引,并且所需的行将分布在整个文件中。无论如何,远低于 ClusteredIndex 的速度,并且由于分布,它将引发表扫描。
-
The AccountTransaction
表仍然很简单(银行账户交易的现实世界概念很简单)。它有一个单一的积极Amount
column.
-
对于每个Account
, the CurrentBalance
is:
-
the AccountStatement.ClosingBalance
上个月的日期,为方便起见,日期为下个月的第一天
- 对于库存而言,
PartAudit.QtyOnHand
-
加上SUM( Transaction.Amounts )
在当月,其中AccountTransactionType
表示存款
- 对于库存而言,
PartMovement.Quantity
-
减去SUM( Transaction.Amount )
在当月,其中AccountTransactionType
表示提款
-
(下面提供代码)。
-
在该方法中,AccountTransactions
仅当月处于不断变化的状态,因此它们必须找回。之前的所有月份均已发布并关闭,因此审计数据AccountStatement.ClosingBalance
必须使用.
-
中较旧的行AccountTransaction
表可以被清除。公共资金超过十年,否则五年,爱好俱乐部系统一年。
-
当然,与会计系统相关的任何代码都必须使用真正的 OLTP 标准和真正的 SQL ACID 事务(在假装 SQL 免费软件中不可能)。
-
此设计包含所有范围级性能考虑因素(如果这不明显,请要求扩展)。数据库内部的扩展不是问题,任何剩余的扩展问题实际上都在数据库外部。
纠正建议
需要说明这些项目只是因为许多 SO 答案中提供了不正确的建议(当然,并由群众民主投票),并且互联网上充满了不正确的建议(业余爱好者喜欢发布他们的主观“真相”):
-
显然,有些人不明白我用技术术语给出了一种方法,用于针对清晰的数据模型进行操作。因此,它不是针对特定国家/地区的特定应用程序的伪代码。该方法适用于有能力的开发人员,对于需要手动引导的人员来说,它不够详细。
-
他们也不明白一个月的截止期是一个example:如果税务局的截止日期是季度,那么请务必使用季度截止日期;如果您唯一的法律要求是年度,请使用年度。
-
即使出于外部或合规目的,您的截止日期是每季度一次,公司也可能出于内部审计和理智目的而选择每月一次的截止日期(即,将变化状态的时间长度保持在最低限度) 。
例如。在澳大利亚,税务局对企业的截止是每季度一次,但较大的公司每月都会取消库存控制(这样就不必在很长一段时间内追查错误)。
例如。银行每月都有法律合规要求,因此他们每月对数据进行内部审计并结账。
-
在原始国家和流氓国家,银行将其流动状态期保持在最长,显然是出于邪恶的目的。其中一些只每年制作合规报告。这就是澳大利亚银行不倒闭的原因之一。
-
In the AccountTransaction
表中,请勿在金额列中使用负数/正数。金钱总是有正值,不存在负二十美元(或者说负二十美元)这样的东西。你欠我五十美元),然后找出双重否定的含义。
-
移动方向,或者你打算用资金做什么,是一个独立且离散的事实(对于AccountTransaction.Amount
)。这需要一个单独的列(一个数据中的两个事实违反了规范化规则,其结果是它给代码带来了复杂性)。
-
实施一个AccountTransactionType
参考表,其主键是( D, W
)作为存款/取款的起点。随着系统的增长,只需添加 (A, a, F, w
) 调整信用;调整借方;银行费用; ATM取款; ETC。
-
无需更改代码。
-
在一些原始国家,诉讼要求规定,在列出交易的任何报告中,每一行都必须显示运行总计。 (请注意,这不是审计要求,因为这些要求优于[(参见上面的方法)法院要求;审计师比律师稍微不那么愚蠢;等等)
显然,我不会反对法院的要求。问题是原始编码员将其翻译为:哦,哦,我们必须实施一个 AccountTransaction.CurrentBalance
column。他们不明白:
-
在报告上打印列的要求并不要求在数据库中存储值
-
任何类型的运行总计都是派生值,并且很容易编码(如果这对您来说不容易,请提出问题)。只需实现报告中所需的代码即可。
-
实施运行总计,例如AccountTransaction.CurrentBalance
作为一个列会导致可怕的问题:
-
在上述案例中,提交给法院使用的报告现已过时(每份在线数据报告在打印时即已过时)。 IE。打印;审查;更改交易;重印;重新审视,直到你满意为止。无论如何都是没有意义的。
-
这就是为什么在不太原始的国家,法院不接受任何旧的印刷纸,他们只接受公布的数字,例如。银行对账单已符合审计要求(请参阅上述方法),并且不能撤回、更改和重新打印。
Comments
Alex:
是的,代码很好看,谢谢。即使是一个样本“投机商号”,让人们可以永远看到起始模式,也会让世界变得更加美好。
对于上面的数据模型。
代码 • 报告当前余额
SELECT AccountNo,
ClosingDate = DATEADD( DD, -1 Date ), -- show last day of previous
ClosingBalance,
CurrentBalance = ClosingBalance + (
SELECT SUM( Amount )
FROM AccountTransaction
WHERE AccountNo = @AccountNo
AND TransactionTypeCode IN ( "A", "D" )
AND DateTime >= CONVERT( CHAR(6), GETDATE(), 2 ) + "01"
) - (
SELECT SUM( Amount )
FROM AccountTransaction
WHERE AccountNo = @AccountNo
AND TransactionTypeCode NOT IN ( "A", "D" )
AND DateTime >= CONVERT( CHAR(6), GETDATE(), 2 ) + "01"
)
FROM AccountStatement
WHERE AccountNo = @AccountNo
AND Date = CONVERT( CHAR(6), GETDATE(), 2 ) + "01"
通过对交易日志进行非规范化,当我添加更多交易类型时,我可以用规范形式换取更方便的查询,并减少视图/物化视图的变化
神救救我。
-
当你违反标准时,你就把自己置于第三世界的位置,那些在第一世界国家不应该损坏、永远不会损坏的东西都会损坏。
从权威那里寻求正确的答案,然后反对它,或者为你不合标准的方法辩护,这可能不是一个好主意。
-
非规范化(此处)会导致更新异常(可以从 TransactionTypeCode 派生的重复列)。您想要轻松编码,但您愿意在两个地方编码,而不是一个地方。这正是容易出错的代码。
根据 E F Codd 博士的说法完全规范化的数据库关系模型提供最简单、最符合逻辑、最直接的代码。 (在我的工作中,我通过合同保证每份报告都可以由一个单一的服务提供SELECT
.)
-
ENUM
不是 SQL。 (免费软件 NONsql 套件没有 SQL 合规性,但它们确实具有 SQL 中不需要的额外功能。)如果您的应用程序升级到商业 SQL 平台,您将不得不重新编写所有这些ENUMs
与普通的查找表一样。与一个CHAR(1)
or a INT
作为PK。然后你就会体会到,这其实是一张有PK的桌子。
-
错误的值为零(它也会产生负面后果)。真理的价值为一。我不会用一换零。因此,这不是一种权衡。这只是你的发展决定。