与我们普通的银行账户一样,我们有许多交易会导致资金的流入或流出。账户余额可以通过简单地加总交易值来计算得出。那么,在数据库中存储更新后的账户余额,还是在需要时重新计算它,哪个更好呢?
每个账户预期的交易量:<每天5笔。
预期检索账户余额:每次发生交易时以及平均每天一次。
与我们普通的银行账户一样,我们有许多交易会导致资金的流入或流出。账户余额可以通过简单地加总交易值来计算得出。那么,在数据库中存储更新后的账户余额,还是在需要时重新计算它,哪个更好呢?
每个账户预期的交易量:<每天5笔。
预期检索账户余额:每次发生交易时以及平均每天一次。
存在客观真相:审计要求。此外,当处理公共资金时,必须遵守法规。
您不必实现完整的会计要求,只需实现您需要的部分即可。
相反,如果实现了与标准会计要求(其中的部分)不同的内容,那么当错误数量或负载超过某个阈值,或者系统扩展时,则会导致重新实现。这是可以避免的成本,因此应该避免。
还需要指出的是:不要雇用没有资质和认证的“审计员”。这将产生后果,就像雇用没有资质的开发人员一样。如果税务局对您处以罚款,情况可能会更糟。
在不太原始的国家中,标准会计方法是这样的。在其他国家,则是“最佳实践”。
此方法适用于具有类似操作、需要、历史月度数字与当前月份要求的任何系统,如库存控制等。
首先,考虑事项。
永远不要重复数据。
如果“当前余额”可以被推导出来(就像你指出的那样很简单),就不要用汇总列重复它。
如果您确实使用了汇总列,则插入新的“账户交易”时,汇总列“当前余额”的值就会过时,因此必须始终更新它。 这是“更新异常”的结果。 这消除了其意义。
外部发布。
另外一点,如果余额被发布,例如在月度银行对账单中,这些文件通常存在法律限制和影响,因此发布的当前余额值在发布后不得更改。
在发布日期之后,在数据库中更改已经发布的数字是不诚实、欺诈等行为的证据。
您不希望银行在2015年4月更改他们在2014年12月向您发布的“当前余额”。
该数字必须被视为审计数字,已经发布且不可更改。
要更正过去发生的错误的“账户交易”,正在进行的更正或调整,必须作为当月的新“账户交易”进行(即使它适用于某个以前的月份或持续时间)。
这是因为适用月份已关闭; 审计完成; 并且已发布,因为一旦历史发生并记录下来后,就无法更改历史。 唯一有效的月份是当前月份。
对于利息计算系统等不那么原始的国家,在发现错误后,如果具有历史影响(例如,您在2015年4月发现自12月2014以来每月计算的证券利息不正确),则更正的利息付款/扣除的价值将根据错误天数今天计算,并作为新的“账户交易”插入当月。 再次强调,唯一有效的月份是当前月份。
当然,还必须纠正安全的利率,以避免重复此错误。
相同的原则适用于库存控制系统。 它保持清晰明了。
所有真正的会计系统(即在适用国家获得审计机构认可的系统,而不是充斥着假货的“软件包”)都使用“双向记账”系统进行所有“账户交易”,这正是因为它可以防止一系列错误,其中最重要的是资金不会丢失。 这需要总分类帐和双向记账。
我所有的数据模型都是使用自1993年以来建模关系数据库的标准IDEF1X呈现的。
我的IDEF1X入门对于那些初次接触关系模型或其建模方法的人来说是必读的。请注意,IDEF1X模型非常详细和精确,显示了所有必需的细节,而自行开发的模型则远不如此。这意味着必须理解符号说明。
对于每个 AccountNo
,每个月将有一个 AccountStatement
行,包括一个 ClosingBalance
、账单 Date
(通常是下个月的第一天)以及关闭月份的其他账单细节。
这不是“重复”或可派生值,因为 (a) 该值仅适用于一个 Date
,(b) 它是出于审计和健全性目的存储的,(c) 它提供了实质性的性能优势(消除了所有交易的 SUM())。
对于库存,对于每个 PartCode
,每月将有一个 PartAudit
行,其中还有一个 QtyOnHand
列。
它具有附加值,约束了需要查询的交易行的范围为当前月份。
同样地,如果您的表是关系型的,并且您有一个 SQL 平台,则 AccountTransaction
的主键将是 (AccountNo
, Transaction DateTime
),这将以毫秒速度检索交易。
而对于记录文件系统,“主键”将是 AccountTransactionID
,并且您将通过交易日期检索当前月份,可能或可能没有正确地进行索引,并且所需的行将分布在文件中。无论如何都比聚集索引速度慢得多,并且由于分散,它将产生表扫描。
AccountTransaction
表保持简单(银行账户交易的现实概念是简单的)。它有一个单一的正数 Amount
列。
对于每个 Account
,CurrentBalance
是:
上个月的 AccountStatement.ClosingBalance
,方便起见日期为下个月的第一天
PartAudit.QtyOnHand
加上当前月份的 SUM( Transaction.Amounts )
,其中 AccountTransactionType
表示存款
PartMovement.Quantity
减去当前月份的 SUM( Transaction.Amount )
,其中 AccountTransactionType
表示取款
(以下提供代码)。
在此方法中,当前月份的 AccountTransactions
处于不确定状态,因此必须检索。所有之前的月份都已经发布并关闭,因此必须使用审计数字 AccountStatement.ClosingBalance
。
可以清除 AccountTransaction
表中的旧行。公共资金超过十年,其他资金超过五年,业余俱乐部系统超过一年。
当然,任何与会计系统相关的代码都必须使用真正的 OLTP 标准和真正的 SQL ACID 事务(在假SQL免费软件中不可能)。
该设计包含了所有范围级性能考虑因素(如果这不明显,请要求扩展)。数据库内的扩
这些内容需要说明,因为在许多SO答案中提供了不正确的建议(当然是由大众民主投票支持的),而互联网上充斥着不正确的建议(业余爱好者喜欢发布他们的主观“真理”):
我已经给出了一种技术术语的方法,可以针对清晰的数据模型进行操作。因此,它不是针对特定国家特定应用程序的伪代码。该方法适用于有能力的开发人员,对于需要手把手指导的人来说不够详细。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"
通过对交易日志进行去规范化,我可以以更方便的查询和更少的视图/物化视图更改来交换正常形式,当我添加更多的tx类型时。
天哪,帮帮我吧。
当你违反标准时,你会把自己置于第三世界的位置,那里的东西本不应该破裂,在第一世界国家从来没有破裂过。因此,这主要取决于项目的需求,在当前开发时间最好花费的地方,以及现在是否值得未来证明解决方案,而不是在性能和可扩展性成为真正的问题时实施第二种方法。
CASE
语句吗?没有额外的代码需要调用。实际上,检查两个列比检查一个列需要更多的代码。 - PerformanceDBAD
。数据模型不显示GeneralLedger(内部银行账户),例如BankCash
。它不是同一张表,会有更多的列。这些账户取决于报告:如果你在计算现金,那就是那个。Asset/Liability/etc
定义了处理方式,以及其外部的AccountNo
。你对SUM()的概念不适用。运算符适用。 - PerformanceDBAAccountType
是外部{支票,储蓄...},而不是内部银行GL帐户类型。请提出一个新问题,以便我可以全面处理它。2nd) 是的。每个4部分账户::GL集合都是ACID事务,但是“所有月费”不是(它是批处理作业)。 - PerformanceDBA