【问题标题】:Database design: Calculating the Account Balance数据库设计:计算账户余额
【发布时间】:2011-05-21 09:54:05
【问题描述】:

如何设计数据库来计算账户余额?

1) 目前我从交易表中计算账户余额 在我的交易表中,我有“描述”和“金额”等。

然后,我会将所有“金额”值相加,然后计算出用户的帐户余额。


我把这个给我的朋友看,他说这不是一个好的解决方案,当我的数据库增长时它会变慢????他说我应该创建单独的表来存储计算出的账户余额。如果这样做,我将不得不维护两个表,并且有风险,帐户余额表可能会不同步。

有什么建议吗?

编辑:选项 2:我是否应该在我的交易表“余额”中添加一个额外的列。 现在我不需要通过很多行数据来执行我的计算。

示例 约翰购买了 100 美元的信用,他借了 60 美元,然后又增加了 200 美元的信用。

金额 100 美元,余额 100 美元。

金额 - 60 美元,余额 40 美元。

金额 200 美元,余额 240 美元。

【问题讨论】:

  • 您预期的交易量是多少?
  • wtf 是每个人都得到负分的问题吗??
  • 不知道iDevelop,我没有给任何人正面或负面的观点:),现在我很困惑,一个人说是,另一个人说不是。到底是怎么回事! ??
  • Transactions 表中 Balance 字段的问题在于,您不仅在行级别构建了一个传递依赖项,这不是“正常”,而且您在列级别添加了一个传递依赖项,这将是如果您遇到问题(触发失败或其他),会很头疼。我的建议是写下你的规范化结构,然后写下你计划拥有的每个“用例”,与其他人讨论,然后根据你的用例检查你的结构,看看是否有一些非规范化是必须的。无论如何,设计阶段至关重要,在那个时候花点时间并不是“浪费”时间!
  • “前几年的交易永远不会被删除”......我会对此做两次。您可能会考虑在一段时间后将旧事务移至存档表,+ 在活动表中创建特殊类型的事务(initialBalance)。这可能是年度流程(或任何适当的时间框架)的一部分。你应该在你的“用例”中包含这一点;-)

标签: sql-server database sql-server-2008 database-design


【解决方案1】:

我的方法是将借方存储在借方列中,贷方存储在贷方列中,并在获取数据时创建两个数组,借方和贷方数组。然后继续将选定的数据附加到数组并为 python 执行此操作:

def real_insert(arr, index, value):
    try:
        arr[index] = value
    except IndexError:
        arr.insert(index, value)


def add_array(args=[], index=0):
    total = 0
    if index:
        for a in args[: index]:
            total += a
    else:
        for a in args:
            total += a
    return total

然后

for n in range(0, len(array), 1):
    self.store.clear()
    self.store.append([str(array[n][4])])
    real_insert(self.row_id, n, array[n][0])
    real_insert(self.debit_array, n, array[n][7])
    real_insert(self.credit_array, n, array[n][8])
    if self.category in ["Assets", "Expenses"]:
        balance = add_array(self.debit_array) - add_array(self.credit_array)
    else:
        balance = add_array(self.credit_array) - add_array(self.debit_array)

【讨论】:

    【解决方案2】:

    这是一种数据库设计,我只有一个表来存储操作/事务的历史记录。目前在许多小型项目中担任魅力。

    这不会取代特定的设计。这是适用于大多数应用的通用解决方案。

    id:int 标准行ID

    操作类型:int 操作类型。支付、收取、利息等

    source_type:int 从哪里进行操作。 目标表或类别:用户、银行、提供商等

    source_id:int 数据库中源的id

    target_type:int 应用到什么操作。 目标表或类别:用户、银行、提供商等

    target_id:int 数据库中目标的id

    金额:十进制(19,2 有符号) 价格值的正负相加

    account_balance:十进制(19,2 签名) 结果余额

    extra_value_a:decimal(19,2 signed) [这是不使用字符串存储的最通用选项] 您可以存储一个额外的数字:利息百分比、折扣、减少等。

    created_at:时间戳

    对于 source_type 和 target_type,最好使用枚举或表 appart。

    如果您想要一个特定的余额,您可以只查询按 created_at 降序到 1 排序的最后一个操作。您可以按源、目标、操作类型等查询。

    为了获得更好的性能,建议将当前余额存储在所需的目标对象中。

    【讨论】:

      【解决方案3】:

      这里想建议您如何以一种非常简单的方式存储您的期初余额:-

      1. 在事务表上创建触发器函数,仅在更新或插入后调用。

      2. 在名为期初余额的账户主表中创建一个具有名称的列。

      3. 将您的期初余额以数组形式保存在主表的期初余额列中。

      4. 你甚至不需要使用服务器端语言使用这个存储数组,你可以使用 PostgreSQL 中可用的数据库数组函数。

      5. 当您想重新计算数组中的期初余额时,只需使用数组函数对您的交易表进行分组并更新主表中的全部数据。

      我已经在 PostgreSQL 中完成了这项工作并且工作正常。

      在您的事务表变得繁重的时间段内,您可以根据日期对事务表进行分区以加快性能。 这种方法非常简单,不需要使用任何额外的表,如果连接表会降低性能,因为连接中较小的表会给你带来高性能。

      【讨论】:

      • 嗨,我不清楚 3 号。 “期初余额”是在同一个交易表中还是在单独的表中?
      【解决方案4】:

      此问题的常见解决方案是在快照模式中维护(例如)每月期初余额。可以通过将当月的交易数据添加到每月期初余额中来计算当前余额。这种方法通常在帐户包中采用,尤其是在您可能需要进行货币兑换和重估的情况下。

      如果您遇到数据量问题,您可以归档旧余额。

      此外,如果您没有专用的外部数据仓库或系统上的管理报告工具,余额对于报告也很有用。

      【讨论】:

        【解决方案5】:

        当然你需要在每一行存储你当前的余额,否则太慢了。为了简化开发,您可以使用约束,这样您就不需要触发器和定期检查数据完整性。我在这里描述过Denormalizing to enforce business rules: Running Totals

        【讨论】:

        • 我知道你喜欢你的记录归档系统,里面有混凝土。
        【解决方案6】:

        一个从未被优雅解决的古老问题。

        我使用过的所有银行软件包都将余额存储在帐户实体中。从运动历史中即时计算它是不可想象的。

        正确的做法是:

        • 移动台有一个“开口” 每个帐户的余额交易。你需要 几年后,当你 需要将旧动作移出 历史活动表 表。
        • 账户实体有余额 字段
        • 机芯上有触发器 更新帐户的表 贷方和借方账户的余额。显然,它有承诺 控制。如果你没有触发器,那么需要有一个独特的模块,它在承诺控制下写入动作
        • 您有一个“安全网”计划 可以离线运行,重新计算 所有天平和显示器(和 可选地更正)错误 余额。这对于 测试。

        某些系统将所有移动存储为正数,并通过反转 from/to 字段或使用标志来表示贷方/借方。就个人而言,我更喜欢贷方字段、借方字段和签名金额,这使得撤销更容易遵循。

        请注意,这些方法适用于现金和证券。

        证券交易可能要复杂得多,尤其是对于公司行为而言,您将需要容纳更新一个或多个买卖双方现金余额、他们的证券头寸余额以及可能的经纪人/存款人的单一交易。

        【讨论】:

        • “就我个人而言,我更喜欢贷方字段、借方字段和签名金额,这使得冲销更容易遵循。”,为什么不只有一个带符号的“金额”字段?跨度>
        • @001 每笔交易包含 3 个字段。 1/ 借记账户的 ID。 2/ 被记入账户的 ID。 3/ 金额,正数=从债权人转移到借方,负数=从借方转移到债权人(通常是冲销)。我建议你阅读en.wikipedia.org/wiki/Double-entry_bookkeeping_system
        • @001 那是2个动作。 1/ X 将 5000 贷给 Y。 2/ X 向 Z 支付 500 税。必须这样做,因为稍后,有人会问“显示所有付款到税帐户 Z”,而您不希望 5000 在那个答案。我真的建议你做一些关于簿记的背景阅读,你会发现它很有帮助
        • 我认为讨论超出了范围,因此我开辟了一个新主题来解决这个问题。 stackoverflow.com/questions/4415022/…
        • 这个答案不正确,依赖于代码,而且很麻烦。对于(a)符合审计要求和立法的方法,(b)不需要需要触发器;离线安全网;重复的列;重复的数据,并且 (c) 无论表格填充如何,都表现良好,请查看 this Answer
        【解决方案7】:

        简单的答案:三个都做。

        存储当前余额;并在每笔交易中存储该时间点的当前余额的移动和快照。这将在任何审计中提供额外的内容。

        我从未在核心银行系统上工作过,但我曾在投资管理系统上工作过,根据我的经验,这就是它的完成方式。

        【讨论】:

        • 这提供了一些额外的来在审计中进行核对。这有助于审计软件(在正确计算和检查事务是否被删除方面;并且不是要求=唯一的方法),我工作的地方是 IT 审计的一部分;但这与会计审计没有直接关系(IT审计只是财务审计的一部分)。
        • 正确,但是对于大型互操作系统来说,遇到某些进程(例如隔夜批处理)产生异常的情况并非不可能;一些非规范化,将有助于确保它们可以被追踪。
        • @Dog Ears,我明白了 - 所以你的大部分条目来自数据交换/互操作,然后 IT 审计是财务审计中更强大的组成部分。有趣的。我仍然保持我的立场:这是一个额外的控制价值,将存在于所有交易中。如果我想审核数据交换过程,我会集中控制它(使用某种数据交换的校验和:由发起方计算并简单地接收,根据接收格式的接收数据计算 - 检查传输的完整性,根据导入的数据计算 - 检查您的导入程序)
        • @smirkingman 为了社区的利益,我的建议有什么糟糕之处,与您的建议有何不同?看来我们基本上提出了同样的建议?
        • @dogs 耳朵可怕的是在每个动作中存储平衡。出现更多重复数据和解决问题时的噩梦。
        【解决方案8】:

        您应该存储当前帐户余额并随时更新。交易表只是过去发生的事情的记录,不应该仅仅为了获取当前余额而频繁使用。考虑到许多查询不仅需要余额,他们还想按它们进行过滤、排序和分组等。将您在复杂查询中创建的每个事务求和的性能损失甚至会削弱规模适中的数据库.

        对这对表的所有更新都应该在一个事务中,并且应该确保所有内容保持同步(并且帐户永远不会透支超过其限制)或事务回滚。作为一项额外措施,您可以运行定期检查的审计查询。

        【讨论】:

        • 告诉来审计你的簿记的人。你会惊讶他们笑了多久。必须对帐户的每笔交易进行编号(以维持订单),您只需将新帐户余额放在那里即可。实际上不需要第二张桌子。没有性能损失。双打,顺便说一句,簿记是如何完成的。这就是全部。
        • @TomTom:现在我明白你的意思了。你的想法很好。太糟糕了,你的答案没有明确说明,太糟糕了,你的答案看起来太激进了!
        • @TomTom,Marcelo 的措辞有点模棱两可,但是您的 cmet 在审计方面没有帮助,也不对。审计是关于建立正确性,与优化速度无关。 Marcelo 有点不精确,因为他谈论的是“当前余额”,实际上大多数金融系统将根据账户和其他分析维度保持余额,这些维度由特定的日期粒度总结。对于需要过滤除符合预订顺序的属性之外的任何事务的任何报告,您在事务级别保持运行平衡的想法是无用的。
        【解决方案9】:

        你的朋友是错的,你是对的,我建议你现在不要改变。
        如果您的数据库因此而变慢,并且在您验证了所有其余部分(正确索引)之后,可能会使用一些非规范化。
        然后,您可以在 Accounts 表中放置一个 BalanceAtStartOfYear 字段,并仅汇总今年的记录(或任何类似方法)。
        但我当然不会预先推荐这种方法。

        【讨论】:

        • 啊……不,说真的。会计是一项棘手的业务,可能需要满足一些严格的法律要求。临时总结并没有削减它。
        • @TomTom。他们当然会这样做,如果交易是根据会计准则实施的(你的不是),在这种情况下,“临时”是错误的。此外,在每一行中复制 CurrentBalance 是非常愚蠢的:当必须进行调整时,必须更新大量行。
        猜你喜欢
        • 2012-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-14
        • 2015-08-31
        相关资源
        最近更新 更多