数据库设计:计算账户余额
如何设计数据库来计算账户余额?
1)目前我从交易表计算账户余额 在我的交易表中,我有“描述”和“金额”等。
然后,我将添加所有“金额”值,这将计算出用户的帐户余额。
我向我的朋友展示了这个,他说这不是一个好的解决方案,当我的数据库增长时,它会变慢???他说我应该创建单独的表来存储计算出的帐户余额。如果这样做,我将不得不维护两个表,并且有风险,帐户余额表可能会不同步。
有什么建议吗?
编辑:选项2:我应该在交易表“余额”中添加一个额外的列吗? 现在我不需要遍历许多行数据来执行计算。
例子 约翰购买了 100 美元的信贷,他借了 60 美元,然后他增加了 200 美元的信贷。
金额 100 美元,余额 100 美元。
金额 - 60 美元,余额 40 美元。
金额 200 美元,余额 240 美元。
How do I design the database to calculate the account balance?
1) Currently I calculate the account balance from the transaction table
In my transaction table I have "description" and "amount" etc..
I would then add up all "amount" values and that would work out the user's account balance.
I showed this to my friend and he said that is not a good solution, when my database grows its going to slow down???? He said I should create separate table to store the calculated account balance. If did this, I will have to maintain two tables, and its risky, the account balance table could go out of sync.
Any suggestion?
EDIT: OPTION 2: should I add an extra column to my transaction tables "Balance".
now I do not need to go through many rows of data to perform my calculation.
Example
John buys $100 credit, he debt $60, he then adds $200 credit.
Amount $100, Balance $100.
Amount -$60, Balance $40.
Amount $200, Balance $240.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
这是一个从未得到优雅解决的古老问题。
我使用过的所有银行软件包都将余额存储在帐户实体中。根据运动历史即时计算它是不可想象的。
正确的做法是:
每个账户的余额交易。你需要
几年后当你
需要将旧的机芯移出
活动运动表到历史记录
桌子。
场上
更新帐户的表
贷方和借方账户的余额。显然,它有承诺
控制。如果你没有触发器,那么就需要一个独特模块来在承诺控制下写入动作
可以离线运行,重新计算
所有天平和显示屏(以及
(可选地更正)错误的
余额。这对于
测试。
某些系统将所有移动存储为正数,并通过反转“起始”/“终止”字段或使用标志来表示贷方/借方。就我个人而言,我更喜欢贷方字段、借方字段和签名金额,这使得冲销更容易遵循。
请注意,这些方法同时适用于现金和证券。
证券交易可能会更加棘手,特别是对于公司行为,您将需要适应一项更新一个或多个买方和卖方现金余额、其证券头寸余额以及可能的经纪人/存款人的交易。
An age-old problem that has never been elegantly resolved.
All the banking packages I've worked with store the balance with the account entity. Calculating it on the fly from movement history is unthinkable.
The right way is:
balance' transaction for each and every account. You'll need
this in a few year's time when you
need to move old movements out of the
active movement table to a history
table.
field
table which updates the account
balances for the credited and debited accounts. Obviously, it has commitment
control. If you can't have a trigger, then there needs to be a unique module which writes movements under commitment control
can run offline, which re-calculates
all the balances and displays (and
optionally corrects) erroneous
balances. This is very useful for
testing.
Some systems store all movements as positive numbers, and express the credit/debit by inverting the from/to fields or with a flag. Personally, I prefer a credit field, a debit field and a signed amount, this makes reversals much easier to follow.
Notice that these methods applies both to cash and securities.
Securities transactions can be much trickier, especially for corporate actions, you will need to accommodate a single transaction that updates one or more buyer and seller cash balances, their security position balances and possibly the broker/depository.
这是我得到的数据库设计,只有一个表用于存储操作/事务的历史记录。目前正在许多小型项目中发挥魅力。
这不会取代特定的设计。这是一个通用的解决方案,可以适合大多数应用程序。
id:整数
标准行 ID
operation_type:int
操作类型。支付、收取、利息等
source_type:int
从操作进行的地方。
目标表或类别:用户、银行、提供商等
source_id:int
数据库中源的 id
target_type:int
操作所应用的内容。
目标表或类别:用户、银行、提供商等
target_id:int
数据库中目标的 id
金额:decimal(19,2signed)
价格值正数或负数相加
account_balance:decimal(19,2signed)
生成的余额
extra_value_a:decimal(19,2signed) [这是不使用字符串存储的最通用的选项]
您可以存储额外的数字:利息百分比、折扣、减少等。
created_at:timestamp
对于 source_type 和 target_type,最好使用枚举或表格设备。
如果您想要特定的余额,您可以只查询按created_at降序限制到1排序的最后一个操作。您可以按源、目标、操作类型等查询。
为了获得更好的性能,建议将当前余额存储在所需的目标对象中。
This is a database design I got with only one table for just storing a history of operations/transactions. Currently working as charm on many small projects.
This doesn't replace a specific design. This is a generic solution that could fit most of the apps.
id:int
standard row id
operation_type:int
operation type. pay, collect, interest, etc
source_type:int
from where the operation proceeds.
target table or category: user, bank, provider, etc
source_id:int
id of the source in the database
target_type:int
to what the operation is applied.
target table or category: user, bank, provider, etc
target_id:int
id of the target in the database
amount:decimal(19,2 signed)
price value positive or negative to by summed
account_balance:decimal(19,2 signed)
resulting balance
extra_value_a:decimal(19,2 signed) [this was the most versatile option without using string storage]
you can store an additional number: interest percentage, a discount, a reduction, etc.
created_at:timestamp
For the source_type and target_type it would be better to use an enum or tables appart.
If you want a particular balance you can just query the last operation sorted by created_at descending limit to 1. You can query by source, target, operation_type, etc.
For better performance it's recommended to store the current balance in the required target object.
您应该存储当前帐户余额并始终保持最新状态。交易表只是过去发生的事情的记录,不应该仅仅为了获取当前余额而高频率使用。考虑到许多查询不仅仅需要余额,它们还希望根据余额进行过滤、排序和分组等。在复杂查询中对您创建的每笔交易进行求和所带来的性能损失甚至会削弱适度大小的数据库。
对这对表的所有更新都应该在一个事务中,并且应该确保所有内容保持同步(并且帐户永远不会透支超过其限制)或事务回滚。作为一项额外措施,您可以运行审核查询来定期检查这一点。
You should store the current account balance and keep it up to date at all times. The transaction table is just a record of what has happened in the past and shouldn't be used at a high frequency just to fetch the current balance. Consider that many queries don't just want balances, they want to filter, sort and group by them, etc. The performance penalty of summing every transaction you've ever created in the middle of complex queries would cripple even a database of modest size.
All updates to this pair of tables should be in a transaction and should ensure that either everything remains in sync (and the account never overdraws past its limit) or the transaction rolls back. As an extra measure, you could run audit queries that check this periodically.
当然,您需要将当前余额存储在每一行中,否则速度太慢。为了简化开发,您可以使用约束,这样您就不需要触发器和定期检查数据完整性。我在这里描述了反规范化以强制执行业务规则:运行总计
Of course you need to store your current balance with each row, otherwise it is too slow. To simplify development, you can use constraints, so that you dont need triggers and periodic checks of data integrity. I described it here Denormalizing to enforce business rules: Running Totals
此问题的常见解决方案是在快照模式中维护(例如)每月期初余额。可以通过将当月的交易数据添加到每月期初余额来计算当前余额。这种方法通常在帐户包中采用,特别是在可能需要货币兑换和重估的情况下。
如果您遇到数据量问题,您可以归档旧余额。
此外,如果您的系统上没有专用的外部数据仓库或管理报告工具,则余额对于报告也很有用。
A common solution to this problem is to maintain a (say) monthly opening balance in a snapshot schema. Calculating the current balance can be done by adding transactional data for the month to the monthly opening balance. This approach is often taken in accounts packages, particularly where you might have currency conversion and revaluations.
If you have problems with data volume you can archive off the older balances.
Also, the balances can be useful for reporting if you don't have a dedicated external data warehouse or a management reporting facility on the system.
你的朋友错了,你是对的,我建议你现在不要改变事情。
如果您的数据库因此而变慢,并且在验证了所有其余部分(正确的索引)之后,可能会使用一些非规范化。
然后,您可以将 BalanceAtStartOfYear 字段放入“帐户”表中,并仅汇总今年的记录(或任何类似的方法)。
但我肯定不会预先推荐这种方法。
Your friend is wrong and you are right, and I would advise you don't change things now.
If your db ever goes slow because of this, and after you have verified all the rest (proper indexing), some denormalisation may be of use.
You could then put a BalanceAtStartOfYear field in the Accounts table, and summarize only this year records (or any similar approach).
But I would certainly not recommend this approach upfront.
这里建议您如何以非常简单的方式存储期初余额:-
在事务表上创建一个触发器函数,仅在更新或插入后调用。
在帐户主表中创建一个名为“期初余额”的列。
将您的期初余额保存在主表的期初余额列的数组中。
你甚至不需要使用服务器端语言,使用这个存储数组,你可以使用数据库数组函数,比如 PostgreSQL 中提供的函数。
当您想重新计算数组中的期初余额时,只需将您的交易表与数组函数分组并更新主表中的全部数据。
我已经在 PostgreSQL 中完成了这个并且工作正常。
当您的事务表变得很重时,您可以根据日期对事务表进行分区以加快性能。
这种方法非常简单,不需要使用任何额外的表,因为连接表时会降低性能,因为连接中的表越少,性能就越高。
Here is would like to suggest you how can you store your opening balance with a very simple way:-
Create a trigger function on the transaction table to be called only after update or insert.
Create a column having name in the master table of account naming Opening Balance.
save your opening balance in array in the opening balance column in master table.
you even not need to use server side language use this store array simply you can use database array functions like available in PostgreSQL.
when you want to recalculate you opening balance in array just group your transaction table with array function and update the whole data in the master table.
I have done this in PostgreSQL and working fine.
over the period of time when your transaction table will become heavy then you can partition for your transaction table on the base of date to speed up the performance.
this approach is very easy and need not to use any extra table which can slow performance if joining table because lesser table in the joining will give you high performance.
我的方法是将借方存储在借方列中,将贷方存储在贷方列中,并在获取数据时创建两个数组,即借方和贷方数组。然后继续将选定的数据追加到数组中,并为 python 执行此操作:
然后
My approach is to store the debits in a debit column, credit in the credit column and when fetching the data create two arrays, debit and credit array. Then keep appending the selected data to the array and do this for python:
then
实现账户余额的优雅方式是什么?
虽然这是一个老问题,但我发现这个问题反映了数据库设计(甚至更进一步,软件设计)的一些深刻问题。我已经思考这个问题好几年了,所以我在这里写下一些结论。
让我们首先考虑一个简单的链表。您可以添加、删除或修改列表中的元素,还可以检索元素计数。当您添加或删除元素时,应更新元素计数。那么,您认为元素计数应该被存储,还是按需重新计数?
在大多数库的实现中,元素计数是存储的,这是为了性能。但是,这个存储的“属性”永远不会暴露给用户。因此,用户不知道 count 属性是根据需要存储还是重新计算,他们只是通过
length()
或size( )
方法,并相信它返回的计数始终是最新的。这正是我们想要的一种实现账户余额的优雅方式。这里概括了要点:
这就是分层设计和封装的哲学。我在这里描述了它:
现在,重点是如何实现“balances”层。如果它是使用带有
SUM
函数的普通 SQL 视图定义来实现的,那么我们知道会出现性能损失,从而损害扩展。近年来 SQL 出现了一些新功能,可以减轻这种性能损失。例如:窗口函数。我在此处描述了一个带有窗口函数的解决方案。
但这还不够,即使计算余额的时间复杂度是 O(n),我们也希望将其降低到 O(1),因为它的使用频率很高。
因此,唯一的方法是存储(更准确地说,缓存)余额以便快速访问。并且,在需要时,增量更新。即不是将所有交易从零开始求和,而是根据刚刚变化的内容进行计算。
目前,有几种方法可以处理这个问题:
这些方法都不够方便或可靠。例如,如果使用触发器,如何确保存储余额的表永远不会被某人意外修改?我们需要将该表指定为“始终由触发器更新,而不是由人工更新”。 SQL对此没有限制。
我希望将来会出现一个真正优雅的 SQL 工具,也许像“自定义视图”之类的东西,允许程序员自定义该视图更新或存储的方式和时间。但现在,至少我们知道了分层设计的哲学。因此,我为自己的簿记系统这样做:
目前,使用 SQL 视图来计算余额(借助窗口函数),并使这一层足够独立。将来,如果性能损失被证明是无法承受的,或者出现更优雅的方式,请用新的工具替换该层。这将更换平衡层的影响降至最低。
What is the elegant way of implementing account balance?
Although this is an old question, I found that this question reflects some profound problem with database design (or even further, with software design). I've contemplated the problem for several years, so I write some conclusions here.
Let's first think about a simple linked-list. You can add, delete, or modify elements in the list, you can also retrieve the element count. When you add or delete elements, the element count should be updated. So, do you think that the element count should be stored, or re-counted on demand?
In most of the libraries' implementations, the element count is stored, this is for performance. But, this stored "attribute" is never exposed to users. So, users don't know wether that count attribute is stored or re-calculated on demand, they just retrieve this information through a
length()
orsize()
method, and trust that the count it returns is always up-to-date.This is exactly what we want as an elegant way to implement account balance. The gist is summerized here:
This is the philosophy of layered design and encapsulation. I depicted it here:
Now, the focus is how the "balances" layer can be implemented. If it's implemented using a normal SQL view definition with
SUM
function, then we know that there will be a performance penalty which harms scaling.There are a few new features with SQL in recent years that can alleviate this performance penalty. For example: Window functions. I described a solution with window function here.
But that's not enough, even the time complexity for calculating balances is O(n), we want to reduce it to O(1) because it's used so often.
So the only way is to store (more precisely, cache) the balances for quick access. And, when needed, update it incrementally. That is, not to sum up all the transactions from zero, but just calculate based on what has just been changed.
Currently, there are a few ways to handle this:
None of these approaches is convenient or reliable enough. For example, if you use triggers, how can you make sure that the table storing balances can never be accidentally modified by someone? We need to specify this table as "always updated by trigger but not human". SQL has no restriction for this.
I hope that a real elegant tool with SQL will appear in the future, maybe something like "customized view" that allow programmers to customize how and when this view is updated or stored. But for now, at least we know the philosophy of layered design. So, I did this for my own bookkeeping system:
Currently, use a SQL view to calculate balances (having the aid of window functions), and make this layer independent enough. In the future, if the performance penalty proves to be unaffordable, or a more elegant way appears, replace that layer with new implements. This reduces the impact of replacing balance layer to the minimum.
简单的答案:三个都做。
存储当前余额;并在每笔交易中存储该时间点的变动和当前余额的快照。这将在任何审计中提供额外的协调。
我从未从事过核心银行系统的工作,但我从事过投资管理系统的工作,根据我的经验,这就是它的工作原理。
Simple answer: Do all three.
Store the current balance; and in each transaction store the movement and a snapshot of the current balance at that point in time. This would give something extra to reconcile in any audit.
I've never worked on core banking systems, but I have worked on investment management systems, and in my experience this is how It's done.