web-dev-qa-db-ja.com

データベース設計:口座残高の計算

アカウント残高を計算するためのデータベースの設計方法は?

1)現在、取引テーブルから口座残高を計算しています。取引テーブルには、「説明」や「金額」などがあります。

次に、すべての「金額」の値を合計すると、ユーザーの口座残高が計算されます。


私はこれを友人に見せましたが、彼は、私のデータベースが遅くなるとき、それは良い解決策ではないと言ったのですか????彼は、計算された口座残高を保存するために別のテーブルを作成する必要があると言いました。これを行った場合、2つのテーブルを維持する必要があり、そのリスクが高いため、口座残高テーブルが同期しなくなる可能性があります。

なにか提案を?

[〜#〜] edit [〜#〜]:オプション2:トランザクションテーブル "Balance"に列を追加する必要があります。これで、計算を実行するために多くのデータ行を調べる必要がなくなりました。

例ジョンは100ドルのクレジットを購入し、60ドルを借金してから、200ドルのクレジットを追加します。

金額100ドル、残高100ドル。

金額-60ドル、残高40ドル。

金額200ドル、残高240ドル。

62
001

エレガントに解決されたことのない昔からの問題。

私が使用したすべての銀行パッケージは、残高を口座エンティティに保存します。移動履歴からその場で計算することは考えられません。

正しい方法は次のとおりです。

  • 移動テーブルには、アカウントごとに「期首残高」トランザクションがあります。これは、古いムーブメントをアクティブなムーブメントテーブルから履歴テーブルに移動する必要がある数年後に必要になります。
  • アカウントエンティティには残高フィールドがあります
  • 移動テーブルには、貸方および借方勘定の勘定残高を更新するトリガーがあります。明らかに、コミットメント制御があります。トリガーを使用できない場合は、コミットメント制御下で動きを書き込むniqueモジュールが必要です
  • オフラインで実行できる「セーフティネット」プログラムがあり、すべての残高を再計算し、誤った残高を表示(およびオプションで修正)します。これはテストに非常に役立ちます。

一部のシステムでは、すべての動きを正の数として保存し、from/toフィールドを反転するか、フラグを使用してクレジット/デビットを表します。個人的には、クレジットフィールド、デビットフィールド、および署名された金額を好みます。これにより、取り消しがずっと簡単になります。

これらの方法は、現金と証券の両方に適用されることに注意してください。

証券取引は、特にコーポレートアクションの場合、はるかに複雑になる可能性があります。1つまたは複数の買い手と売り手の現金残高、それらの証券ポジションの残高、および場合によってはブローカー/預金を更新する単一の取引に対応する必要があります。

58
smirkingman

現在の口座残高を保存し、常に最新の状態に保つ必要があります。トランザクションテーブルは、過去に起こったことの記録に過ぎず、現在の残高を取得するためだけに頻繁に使用するべきではありません。多くのクエリはバランスを必要としないだけでなく、それらをフィルタリング、ソート、グループ化するなどのことを考慮してください。 。

このテーブルペアへのすべての更新はトランザクション内にある必要があり、すべてが同期したままである(およびアカウントが限度を超えてオーバードローしない)か、トランザクションがロールバックされるようにする必要があります。追加の手段として、これを定期的にチェックする監査クエリを実行できます。

4
Marcelo Cantos

これは、操作/トランザクションの履歴を保存するためのテーブルを1つだけ持つデータベース設計です。現在、多くの小さなプロジェクトの魅力として働いています。

これは特定のデザインを置き換えるものではありません。これは、ほとんどのアプリに適合する汎用ソリューションです。

id:int標準行ID

operation_type:int操作タイプ。支払い、回収、利子など

source_type:int操作の進行元。ターゲット表またはカテゴリー:ユーザー、銀行、プロバイダーなど

source_id:intデータベース内のソースのID

target_type:int操作の適用対象。ターゲット表またはカテゴリー:ユーザー、銀行、プロバイダーなど

target_id:intデータベース内のターゲットのID

amount:decimal(19,2 signed)価格値の合計は正または負

account_balance:decimal(19,2 signed)結果の残高

extra_value_a:decimal(19,2 signed)[これは文字列ストレージを使用しない最も汎用性の高いオプションでした]追加の数値を保存できます:利率、割引、割引など。

created_at:timestamp

Source_typeおよびtarget_typeには、enumまたはテーブルappartを使用することをお勧めします。

特定の残高が必要な場合は、created_atの降順制限でソートされた最後の操作を1に照会するだけです。ソース、ターゲット、operation_typeなどで照会できます。

パフォーマンスを向上させるには、現在の残高を必要なターゲットオブジェクトに保存することをお勧めします。

3
Heroselohim

この問題の一般的な解決策は、スナップショットスキーマで(たとえば)月次の期首残高を維持することです。現在の残高の計算は、月の取引データを月次の期首残高に追加することで実行できます。このアプローチは、特に通貨換算と再評価が必要な場合に、アカウントパッケージでよく使用されます。

データ量に問題がある場合は、古い残高からアーカイブできます。

また、システムに専用の外部データウェアハウスまたは管理レポート機能がない場合、残高はレポートに役立ちます。

もちろん、現在の残高を各行に保存する必要があります。それ以外の場合は遅すぎます。開発を簡素化するために、制約を使用して、トリガーやデータ整合性の定期的なチェックが不要になるようにすることができます。ここで説明しました ビジネスルールを実施するための非正規化:合計の実行

1
A-K

私のアプローチは、借方を借方列に、貸方を貸方列に格納し、データをフェッチするときに借方と貸方の2つの配列を作成することです。次に、選択したデータを配列に追加し続け、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)
0
kafeero

あなたの友人は間違っていて、あなたは正しいです。
このためにデータベースが遅くなり、残りすべて(適切なインデックス付け)を検証した後、非正規化が役立つ場合があります。
次に、AccountsテーブルにBalanceAtStartOfYearフィールドを配置し、今年のレコードのみ(または同様のアプローチ)を要約できます。
しかし、このアプローチを事前に推奨することは確かにありません。

0
Patrick Honorez

非常に簡単な方法であなたの期首残高をどのように保存できるかを提案したいと思います:-

  1. 更新または挿入後にのみ呼び出されるトリガー関数をトランザクションテーブルに作成します。

  2. 口座残高の期首残高のマスターテーブルに名前を持つ列を作成します。

  3. マスターテーブルの期首残高列に配列で期首残高を保存します。

  4. サーバー側の言語を使用する必要はなく、このストア配列を使用するだけで、PostgreSQLで使用可能なデータベース配列関数を使用できます。

  5. 配列の期首残高を再計算する場合は、トランザクションテーブルを配列関数でグループ化し、マスターテーブルのデータ全体を更新するだけです。

これはPostgreSQLで行い、正常に動作しています。

トランザクションテーブルが重くなる期間にわたって、日付を基準にトランザクションテーブルのパーティションを作成して、パフォーマンスを高速化できます。このアプローチは非常に簡単で、テーブルを結合する場合にパフォーマンスを低下させる余分なテーブルを使用する必要はありません。結合のテーブルが少ないほど高いパフォーマンスが得られるためです。

0
Ranjeet