如何设计数据库以计算帐户余额?
1)目前我从交易表中计算账户余额在我的交易表中我有“描述”和“金额”等 .
然后我会将所有“金额”值相加,这将计算出用户的帐户余额 .
我向我的朋友展示了这个,他说这不是一个好的解决方案,当我的数据库增长它会减慢????他说我应该创建单独的表来存储计算的帐户余额 . 如果这样做,我将不得不维护两个表,而且风险很大,帐户余额表可能会不同步 .
有什么建议吗?
EDIT :选项2:我应该在事务表"Balance"中添加一个额外的列 . 现在我不需要经过多行数据来执行我的计算 .
示例约翰购买100美元信贷,他债务60美元,然后他增加200美元信贷 .
金额$ 100,余额$ 100 .
金额 - 60美元,余额40美元 .
金额$ 200,余额$ 240 .
9 回答
您应该存储当前帐户余额并始终保持最新 . 交易表只是过去发生的事情的记录,不应仅用于获取当前余额的高频率 . 考虑到许多查询不只是想要 balancer ,他们想要对它们进行过滤,排序和分组等 . 在复杂查询的中间总结您创建的每个事务的性能损失甚至会削弱适度大小的数据库 .
对这对表的所有更新应该在一个事务中,并且应该确保所有内容保持同步(并且帐户永远不会超过其限制)或事务回滚 . 作为额外措施,您可以运行定期检查此问题的审计查询 .
此问题的常见解决方案是在快照架构中维护(比方说)每月期初余额 . 可以通过将月份的交易数据添加到月度期初余额来计算当前余额 . 这种方法通常在帐户包中使用,特别是在您可能进行货币转换和重估的情况下 .
如果您遇到数据量问题,可以存档旧余额 .
此外,如果您在系统上没有专用的外部数据仓库或管理报告工具,则余额可用于报告 .
当然,您需要在每行存储当前余额,否则速度太慢 . 为了简化开发,您可以使用约束,这样您就不需要触发器和定期检查数据完整性 . 我在这里描述了Denormalizing to enforce business rules: Running Totals
你的朋友错了,你是对的,我建议你不要改变现在的事情 .
如果你的db因此而变慢,并且在你验证了所有其余的(正确的索引)之后,可能会使用一些非规范化 .
然后,您可以在Accounts表中放置一个BalanceAtStartOfYear字段,并仅汇总今年的记录(或任何类似的方法) .
但我当然不会事先推荐这种方法 .
一个从未得到优雅解决的古老问题 .
我使用的所有银行业务包都存储了账户实体的余额 . 从运动历史中动态计算它是不可想象的 .
正确的方法是:
移动表对每个帐户都有一个'opening balance'事务 . 您需要将旧动作从活动移动表移动到历史记录表的时间为'll need this in a few year' .
帐户实体有一个余额字段
移动表上有一个触发器,用于更新贷记和借记帐户的帐户余额 . 显然,它有承诺控制 . 如果你不能有一个触发器,那么需要一个 unique 模块,它在承诺控制下写入动作
您有一个可以离线运行的'safety net'程序,它可以重新计算所有余额并显示(并可选择更正)错误的余额 . 这对测试非常有用 .
有些系统将所有运动存储为正数,并通过反转from / to字段或带有标志来表示信用/借记 . 就个人而言,我更喜欢信用额度,借方字段和签名金额,这使得逆转更容易遵循 .
请注意,这些方法适用于现金和证券 .
证券交易可能要复杂得多,特别是对于公司行为,您需要提供一笔交易,以更新一个或多个买方和卖方的现金余额,其安全头寸余额以及可能的经纪人/存款 .
以下是建议您如何以非常简单的方式存储您的期初余额: -
在事务表上创建一个触发器函数,仅在更新或插入后调用 .
在名为Opening Balance的帐户主表中创建一个具有名称的列 .
在主表的期初余额栏中保存数组中的期初余额 .
你甚至不需要使用服务器端语言使用这个存储数组只是你可以使用像PostgreSQL中可用的数据库数组函数 .
如果要重新计算数组中的开放余额,只需将事务表与数组函数分组,并更新主表中的整个数据 .
我在PostgreSQL中做到了这一点并且工作正常 .
在您的事务表变重的那段时间内,您可以在日期的基础上对事务表进行分区以加快性能 . 这种方法非常简单,不需要使用任何额外的表,如果连接表会降低性能,因为连接中较小的表将为您提供高性能 .
这是一个数据库设计,我只使用一个表来存储操作/事务的历史记录 . 目前在许多小项目中充当魅力 .
这并不能取代特定的设计 . 这是一个适合大多数应用程序的通用解决方案 .
id :int标准行id
operation_type :int操作类型 . 支付,收取,利息等
source_type :执行操作的int . 目标表或类别:用户,银行,提供商等
source_id :数据库中源的int id
target_type :int表示应用的操作 . 目标表或类别:用户,银行,提供商等
target_id :数据库中目标的int id
amount :十进制(19,2签名)价格值正或负相加
account_balance :十进制(19,2签名)产生余额
extra_value_a :十进制(19,2签名)[这是不使用字符串存储的最通用选项]您可以存储一个额外的数字:利息百分比,折扣,减少等 .
created_at :时间戳
对于source_type和target_type,最好使用枚举或表appart .
如果您想要特定的余额,您只需查询按created_at降序限制排序的最后一个操作为1.您可以按来源,目标,operation_type等查询 .
为了获得更好的性能,建议将当前余额存储在所需的目标对象中 .
我的方法是将借记存储在借方列中,信用列中存入贷方,并且在获取数据时创建两个数组,借记和贷记数组 . 然后继续将所选数据附加到数组并为python执行此操作:
然后
简单回答:三个都做 .
存储当前余额;并且在每个交易中存储该时间点的当前余额的移动和快照 . 这将在任何审计中提供额外的协调 .
我从未参与核心银行系统,但我从事过投资管理系统,根据我的经验,这就是它的完成方式 .