因此,它就像我们的正常银行账户一样,我们有很多交易,导致资金流入或流出.账户余额总是可以通过简单地将交易价值相加得出.在这种情况下,将更新后的账户余额存储在数据库中,或者在需要时重新计算,哪种方法更好?
每个账户的预期交易量:<;每天5次
帐户余额的预期取回:每当交易发生时,否则平均每天取回一次.
你建议如何对此做出决定?
因此,它就像我们的正常银行账户一样,我们有很多交易,导致资金流入或流出.账户余额总是可以通过简单地将交易价值相加得出.在这种情况下,将更新后的账户余额存储在数据库中,或者在需要时重新计算,哪种方法更好?
每个账户的预期交易量:<;每天5次
帐户余额的预期取回:每当交易发生时,否则平均每天取回一次.
你建议如何对此做出决定?
前言
有一个客观道理:审计要求.此外,在处理公帑时,立法机关是必须遵守的.
您不必实现全部会计要求,您可以只实现您需要的部分.
相反,实施other than%的标准会计要求(其部分)是不明智的,因为这保证了当错误数量或负载超过某个阈值,或系统扩展时,您将不得不重新实施.这是可以避免的,因此也是应该避免的.
还需要说明的是:不要雇佣没有资质、未经认可的"审计师".这会有后果的,就像你雇了一个不合格的开发人员一样.如果税务局对你罚款,情况可能会更糟.
非原始国家的标准会计方法是这样的.如果你愿意的话,这是其他人的"最佳实践".
此方法适用于任何具有类似操作的系统;需要;历史月度数据与当前月度需求,如库存控制等.
首先,考虑因素.
切勿复制数据.
如果可以导出当前余额(这里很简单),请不要将其与汇总列一起复制.这样的列是数据的重复.它违反了正常化规则.此外,它creates是更新异常,否则该更新异常是不存在的.
如果您确实使用汇总列,则每当更新事务时(如在更改中,而不是在插入新事务时),汇总列value变得过时,因此无论如何它都必须一直更新.这就是更新异常的后果.这就消除了拥有它的价值.
外部出版物.
分开的点.如果余额被公布,就像在每月的银行对账单中一样,这样的文件通常有法律限制和影响,因此公布的当前余额值在公布后不能改变.
在发布日期之后,在数据库中,外部发布的数据的任何变化都是不诚实行为、欺诈等的证据.
你不会希望你的银行在2015年4月改变他们在2014年12月给你的银行对账单中公布的当前余额.
该数字必须被视为已公布且不可更改的审计数字.
为了纠正过go 犯下的错误,也就是现在正在纠正的错误,所需的修正或调整将作为当月的新交易进行(即使它适用于上个月或某个持续时间).
这是因为适用于该月份是关闭的、已审核的和已发布的,因为在发生并记录之后不能更改历史.唯一的effective个月是当期的.
对于计息系统等,在不那么原始的国家,当发现错误时,它具有历史性的影响(例如,您在2015年4月发现证券上计算的利息不正确(自2014年12月以来),今天计算更正后的利息支付/扣减的值,用于错误的天数,并将总和作为交易插入当月.同样,唯一有效的月份是当前月份.
当然,证券的利率也必须修正,这样错误就不会重演.
如果你发现你的银行对你的储蓄(有息)账户利息的计算有误,并加以纠正,你就会得到当月的一笔存款,即构成整个调整值的一笔存款.那是当月的一笔交易.
银行没有:改变历史; for each 历史月份申请利息;回顾历史上的银行对账单;重新发布历史银行对账单.不,除了可能在第三世界国家.
同样的原则也适用于库存控制系统.它保持理智.
所有真实的会计系统(即那些在适用国家获得审计机构认可的公司,而不是比比皆是的米老鼠"套餐",使用复式记账系统进行交易,正是因为它防止了大量错误,其中最重要的是,资金不会"丢失".这需要总账和复式记帐.
This Answer services the Question that is asked, which is not Double-Entry Accounting.
For a full treatment of that subject (detailed data model; examples of accounting Transactions; rows affected; and SQL code examples), refer to this Q&A:
Relational Data Model for Double-Entry Accounting.
影响性能的主要问题超出了这个问题的范围,它们是关于您是否实现了真正的关系数据库(例如.为方便起见,60年代的以Record IDs
为特征的记录归档系统被部署在SQL数据库容器中).
使用真正的关系键等将保持高性能,而与表的数量无关.
相反,RFS的性能会很差,它们根本无法执行."规模"在RFS的上下文中使用时,是一个欺骗性的术语:它隐藏了原因,并试图解决除原因之外的所有问题.最重要的是,这样的系统没有关系系统的关系完整性、关系能力或相对速度.
我所有的数据模型都在100中呈现,这是自1993年以来建立关系数据库模型的标准.
对于那些刚接触Relational Model或其建模方法的人来说,"我的100"是必不可少的读物.请注意,IDEF1X模型具有丰富的细节和精确度,显示了所有需要的细节,而国产模型的细节要少得多.这意味着,这个符号必须被理解.
对于每个帐户,AccountStatement
表中将有一个ClosingBalance
(每月每AccountNo
一行),以及对帐单日期(通常是每月的第一天)和其他对帐单详细信息.
这不是一个副本,因为它是审计和健全的目的要求.
对于库存,它是PartAudit
表中的QtyOnHand
列(每月每PartCode
一行)
它还有一个附加值,因为它限制了当月需要查询to的事务行的范围
同样,如果您的表是关系表,那么AccountTransaction
的主键将是(AccountNo
,事务DateTime
),它将以毫秒的速度检索事务.
而对于记录归档系统,"主键"将是TransactionID
,您将按交易日期检索当前月份,这可能是正确的索引,也可能不是正确的索引,所需的行将分布在整个文件中.在任何情况下,在远低于ClusteredIndex的速度下,由于传播,它将导致表扫描.
AccountTransaction
表仍然很简单(现实世界中银行账户交易的概念很简单).它有一个正Amount
列.
对于每Account
个,CurrentBalance
是:
上个月的AccountStatement.ClosingBalance
号,为方便起见,日期定在下个月的第一天
(库存为PartAudit.QtyOnHand
)
加上当月AccountTransaction.Amounts
的总和,其中TransactionType
表示存款
(对于库存,为PartMovement.Quantity
)
减go 当月的AccountTransaction.Amounts
之和,其中`MovementType表示取款.
在这种方法中,只有当月的AccountTransactions
人处于不断变化的状态,因此他们是must be derived人.之前的所有月份均已公布并结束,因此审计图为must be used.
可以清除AccountTransaction
表中较旧的行.公款十年以上,否则五年,业余俱乐部系统一年.
当然,任何与会计系统相关的代码都必须使用真正的OLTP标准和真正的SQL ACID事务.
此设计包含所有范围级性能考虑因素(如果这一点不明显,请要求扩展).在数据库内伸缩是不成问题的,任何剩余的伸缩问题实际上都在数据库之外.
之所以需要说明这些问题,只是因为在如此多的答案中提供了错误的建议(当然,这些建议是由大众民主投票决定的),而且互联网上充斥着错误的建议(业余爱好者喜欢公布他们的主观"真相"):
很明显,有些人不明白我在技术上给出了一种方法,对一个清晰的数据模型进行操作.因此,它不是针对特定国家/地区的特定应用程序的伪代码.这个方法是针对有能力的开发人员的,对于那些需要手把手带领的人来说还不够详细.
他们也不明白一个月的截止期限是example:如果你的税务局的截止日期是季度,那么一定要使用季度截止日期;如果你唯一的法律要求是年度,那么使用年度.
即使出于外部或合规目的,您的截止日期是季度截止日期,公司也很可能 Select 每月截止日期,以便进行内部审计和保持理智(例如,以将通量状态的周期长度保持在最小).
在澳大利亚,税务局对企业的截止日期是每季度一次,但较大的公司每月都会切断库存控制(这样可以避免长时间追查错误).
银行每月都有法律合规要求,因此他们每月对数字进行内部审计,并结帐.
在原始国家和流氓国家,银行为了明显的邪恶目的,将其流动状态保持在最大值.其中一些公司仅每年提交合规报告.这就是澳大利亚银行不会倒闭的原因之一.
在AccountTransaction
表格中,不要在金额列中使用负数/正数.钱总是有正值的,没有负20美元(或那you owe me minus fifty dollars美元)这回事,然后算出双重否定有别的意思.
移动方向,或者说你打算如何处理资金,是一个独立和离散的事实(对AccountTransaction.Amount
人来说).这需要单独的列(一个数据中的两个事实违反了规范化规则,结果是它给代码带来了复杂性).
实现TransactionType
参照表,其主键为(D, W
),以存取款为起点.随着系统的发展,只需为调整贷方、调整借方、银行手续费、自动柜员机_提款等添加(A, a, F, w
)即可.
无需更改代码.
在一些原始国家,诉讼要求规定,在任何列出交易的报告中,每一行都必须显示运行总数.(注意,这不是审计要求,因为这些要求优于法院要求[(参考上面的方法);审计师比律师稍微没那么笨;等等)
显然,我不会对法院的要求提出异议.问题是,原始编码员会将其转换为:oh, oh, we must implement aAccountTransaction.CurrentBalance
column.他们不明白这一点:
要求在报表上打印一列并不是要求在数据库中存储值
任何类型的运行总和都是派生值,并且很容易编码(如果对您来说不容易,请提出一个问题).只需在报告中实现所需的代码即可.
将运行总数(如AccountTransaction.CurrentBalance
)作为一个列来执行会导致可怕的问题:
引入重复列,因为它是可派生的.打破常态.引入更新异常.
更新异常:每当历史上插入事务或更改AccountTransaction.Amount
时,所有AccountTransaction.CurrentBalances
from that date to the present都必须重新计算和更新.
在上述 case 中,提交给法院使用的报告现在已经过时(每一份在线数据报告在打印出来的那一刻就已经过时).也就是说.打印;查看;更改交易;重新打印;重新查看,直到您满意为止.这在任何情况下都是没有意义的.
这就是为什么在不那么原始的国家,法院不接受任何旧的印刷纸张,他们只接受公布的数字,例如.银行对帐单,已经符合审计要求(请参考上述方法),不能召回、更改和重新打印.
亚历克斯:
是的,代码会很好看,谢谢.即使是一个示例"水桶工厂",这样人们可以一次永远地看到启动模式,也会让世界变得更美好.
对于上面的数据模型.
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"
通过非规范化事务日志(log),当我添加更多TX类型时,我用范式换取了更方便的查询和更少的视图/materialized 视图更改
god 保佑我.
当你违反标准时,你把自己放在第三世界的位置上,在那里,那些不应该打破的东西,那些在第一世界国家永远不会打破的东西,就会破裂.
从权威那里寻求正确的答案,然后反对它,或者为你的不合标准的方法辩护,可能不是一个好主意.
反规范化(此处)会导致更新异常,即重复列,该列可以从TransactionTypeCode派生.你希望编码简单,但你愿意在两个地方编码,而不是一个地方.这正是容易出错的代码类型.
根据E F Codd博士的Relational Model完全标准化的数据库提供了最简单、最逻辑、最直接的代码.(在我的工作中,我签约保证每一份报告都可以由一个SELECT
人提供服务.)
ENUM
不是SQL.(免费的非SQL套件没有SQL遵从性,但它们确实有SQL中不需要的额外功能.)如果你的应用程序毕业于一个商业SQL平台,你将不得不将所有这些ENUMs
作为普通的查找表重新编写.主键是CHAR(1)
或INT
.然后你就会明白,这实际上是一张有PK的桌子.
错误的值为零(也会产生负面后果).真理的价值是1.我不会用1换0.因此,这不是一种权衡.这只是你的发展决定.