Datenbankdesign: Berechnung des Kontostands

76

Wie gestalte ich die Datenbank, um den Kontostand zu berechnen?

1) Derzeit berechne ich den Kontostand aus der Transaktionstabelle. In meiner Transaktionstabelle habe ich "Beschreibung" und "Betrag" usw.

Ich würde dann alle "Betrag" -Werte addieren und das würde den Kontostand des Benutzers berechnen.


Ich habe dies meinem Freund gezeigt und er sagte, dass dies keine gute Lösung ist, wenn meine Datenbank wächst, wird sie langsamer ???? Er sagte, ich sollte eine separate Tabelle erstellen, um den berechneten Kontostand zu speichern. In diesem Fall muss ich zwei Tabellen pflegen, und es ist riskant, dass die Kontostandstabelle nicht mehr synchron ist.

Irgendein Vorschlag?

EDIT : OPTION 2: Sollte ich meiner Transaktionstabelle "Balance" eine zusätzliche Spalte hinzufügen? Jetzt muss ich nicht mehr viele Datenzeilen durchgehen, um meine Berechnung durchzuführen.

Beispiel John kauft einen Kredit von 100 USD, er verschuldet 60 USD und fügt dann einen Kredit von 200 USD hinzu.

Betrag 100 $, Restbetrag 100 $.

Betrag - 60 USD, Restbetrag 40 USD.

Betrag $ 200, Restbetrag $ 240.

001
quelle
1
Was sind Ihre erwarteten Volumina für Transaktionen?
Patrick Honorez
WTF ist diese Frage, wo jeder negative Punkte bekommt?
Patrick Honorez
Keine Ahnung, iDevelop, ich habe niemandem einen positiven oder negativen Punkt gegeben :), jetzt bin ich verwirrt, einer sagt ja, ein anderer sagt nein. Was ist los! ??
001
7
Das Problem mit dem Feld "Saldo" in der Transaktionstabelle besteht darin, dass Sie nicht nur eine transitive Abhängigkeit auf Zeilenebene erstellen, die nicht "normal" ist, sondern auch eine transitive Abhängigkeit auf Spaltenebene hinzufügen, was Kopfschmerzen bereitet, wenn Sie jemals ein Problem haben (Triggerfehler oder andere). Mein Rat ist, Ihre normalisierte Struktur aufzuschreiben, dann jeden geplanten "Anwendungsfall" aufzuschreiben, mit anderen zu diskutieren und dann Ihre Struktur im Lichte Ihrer Anwendungsfälle zu überprüfen, um festzustellen, ob eine Denormalisierung erforderlich ist. Auf jeden Fall ist die Entwurfsphase von entscheidender Bedeutung. Wenn Sie sich an diesem Punkt Zeit nehmen, verlieren Sie keine Zeit!
Patrick Honorez
6
"Transaktionen werden nie aus den Vorjahren gelöscht" ... Ich würde zweimal darüber nachdenken. Sie können die alten Transaktionen nach einiger Zeit in eine Archivtabelle verschieben und + einen speziellen Transaktionstyp (initialBalance) in der aktiven Tabelle erstellen. Dies kann Teil eines jährlichen Prozesses (oder eines angemessenen Zeitrahmens) sein. Und Sie sollten diesen Punkt in Ihre "Anwendungsfälle" aufnehmen ;-)
Patrick Honorez

Antworten:

70

Ein uraltes Problem, das nie elegant gelöst wurde.

Alle Bankpakete, mit denen ich gearbeitet habe, speichern den Kontostand bei der Konteneinheit. Eine schnelle Berechnung aus dem Bewegungsverlauf ist undenkbar.

Der richtige Weg ist:

  • Die Bewegungstabelle enthält für jedes Konto eine Transaktion zum Eröffnungssaldo. Sie benötigen dies in ein paar Jahren, wenn Sie alte Bewegungen aus der aktiven Bewegungstabelle in eine Verlaufstabelle verschieben müssen.
  • Die Kontenentität verfügt über ein Saldofeld
  • In der Bewegungstabelle befindet sich ein Auslöser, der den Kontostand für die gutgeschriebenen und belasteten Konten aktualisiert. Offensichtlich hat es eine Verpflichtungskontrolle. Wenn Sie keinen Auslöser haben können, muss es ein eindeutiges Modul geben, das Bewegungen unter Commitment-Kontrolle schreibt
  • Sie haben ein Sicherheitsnetzprogramm, das Sie offline ausführen können. Es berechnet alle Salden neu und zeigt fehlerhafte Salden an (und korrigiert sie optional). Dies ist sehr nützlich zum Testen.

Einige Systeme speichern alle Bewegungen als positive Zahlen und drücken die Gutschrift / Lastschrift durch Invertieren der Felder von / nach oder mit einem Flag aus. Persönlich bevorzuge ich ein Kreditfeld, ein Lastschriftfeld und einen signierten Betrag. Dies erleichtert die Nachverfolgung erheblich.

Beachten Sie, dass diese Methoden sowohl für Bargeld als auch für Wertpapiere gelten.

Wertpapiertransaktionen können sehr viel schwieriger sein, insbesondere bei Kapitalmaßnahmen. Sie müssen eine einzelne Transaktion durchführen, bei der ein oder mehrere Kassenguthaben von Käufern und Verkäufern, ihre Sicherheitsguthaben und möglicherweise der Broker / Verwahrer aktualisiert werden.

grinsender Mann
quelle
4
"Ich persönlich bevorzuge ein Kreditfeld, ein Lastschriftfeld und einen signierten Betrag. Dies erleichtert die Nachverfolgung erheblich.", Warum nicht einfach 1 Feld "Betrag" mit dem Zeichen?
001
9
@ 001 Jede Transaktion enthält 3 Felder. 1 / Die ID des belasteten Kontos. 2 / Die ID des Kontos, das gutgeschrieben wird. 3 / Der Betrag, positiv = Übertragung von Gläubiger zu Debitor, negativ = Übertragung von Debitor zu Gläubiger (normalerweise eine Umkehrung). Ich schlage vor, Sie lesen en.wikipedia.org/wiki/Double-entry_bookkeeping_system
smirkingman
1
@ 001 Das sind 2 Bewegungen. 1 / X gutschreibt 5000 an Y. 2 / X zahlt 500 Steuern an Z. Dies muss auf diese Weise erfolgen, da später jemand fragt, ob alle Zahlungen auf dem Steuerkonto Z angezeigt werden sollen und Sie die 5000 nicht einlösen möchten diese Antwort. Ich schlage wirklich vor, dass Sie ein bisschen Hintergrundlesung über Buchhaltung machen, Sie werden es sehr hilfreich finden
grinsender
1
@ 001 Wie ich oben vorgeschlagen habe, sollten Sie die Grundlagen der doppelten Buchhaltung lernen
Smirkingman
6
Diese Antwort ist falsch, codeabhängig und umständlich. Für eine Methode, die (a) den Prüfungsanforderungen und dem Gesetzgeber entspricht, (b) keine Auslöser erfordert; Offline-Sicherheitsnetze; doppelte Spalten; Wenn Sie doppelte Daten haben und (c) unabhängig von der Tabellenpopulation eine gute Leistung erbringen, sehen Sie sich diese Antwort an .
PerformanceDBA
4

Sie sollten den aktuellen Kontostand speichern und jederzeit auf dem neuesten Stand halten. Die Transaktionstabelle ist nur eine Aufzeichnung dessen, was in der Vergangenheit passiert ist, und sollte nicht mit hoher Häufigkeit verwendet werden, um nur den aktuellen Kontostand abzurufen. Bedenken Sie, dass viele Abfragen nicht nur Salden wünschen, sondern auch nach ihnen filtern, sortieren und gruppieren möchten usw. Der Leistungsverlust beim Summieren jeder Transaktion, die Sie jemals inmitten komplexer Abfragen erstellt haben, würde sogar eine Datenbank von bescheidener Größe lahm legen .

Alle Aktualisierungen dieses Tabellenpaars sollten sich in einer Transaktion befinden und sicherstellen, dass entweder alles synchron bleibt (und das Konto niemals über sein Limit hinaus gezogen wird) oder die Transaktion zurückgesetzt wird. Als zusätzliche Maßnahme können Sie Überwachungsabfragen ausführen, die dies regelmäßig überprüfen.

Marcelo Cantos
quelle
Sagen Sie, dass die Leute kommen, um Ihre Buchhaltung zu prüfen. Sie werden überrascht sein, wie lange sie lachen. Jede Transaktion auf ein Konto muss nummeriert sein (um eine Bestellung aufrechtzuerhalten) und Sie können einfach den neuen Kontostand direkt dort eingeben. Eigentlich braucht man keinen zweiten Tisch. Keine Leistungsstrafe. Doppelt übrigens, wie Buchhaltung gemacht wird. Worum es hier geht.
TomTom
@ TomTom: Jetzt verstehe ich deinen Standpunkt. Ihre Idee ist gut. Schade, dass es in Ihrer Antwort nicht klar gesagt wurde, und schade, dass Ihre Antworten so aggressiv aussehen!
Patrick Honorez
1
@ TomTom, Marcelos Wortlaut ist etwas mehrdeutig, aber Ihre Kommentare zur Prüfung sind weder hilfreich noch richtig. Beim Audit geht es darum, die Richtigkeit festzustellen, und es hat wenig mit der Optimierung der Geschwindigkeit zu tun. Marcelo ist etwas ungenau, weil er von "aktuellem Saldo" spricht, bei dem in Wirklichkeit die meisten Finanzsysteme Salden nach Konten und anderen analytischen Dimensionen führen, die durch eine bestimmte Datumsgranularität zusammengefasst werden. Ihre Idee, ein laufendes Guthaben auf Transaktionsebene zu halten, ist für jeden Bericht nutzlos, der Transaktionen nach etwas anderem als einem Attribut filtern müsste, das der Reihenfolge der Buchung entspricht.
Unvernunft
4

Dies ist ein Datenbankdesign, das ich mit nur einer Tabelle zum Speichern einer Historie von Operationen / Transaktionen erhalten habe. Derzeit arbeitet er als Charme an vielen kleinen Projekten.

Dies ersetzt kein bestimmtes Design. Dies ist eine generische Lösung, die für die meisten Apps geeignet ist.

id : int Standardzeilen-ID

Operationstyp : int Operationstyp. zahlen, sammeln, verzinsen usw.

source_type : int, von wo aus die Operation fortgesetzt wird . Zieltabelle oder -kategorie: Benutzer, Bank, Anbieter usw.

source_id : int id der Quelle in der Datenbank

Zieltyp : int, auf was die Operation angewendet wird. Zieltabelle oder Kategorie: Benutzer, Bank, Anbieter usw.

target_id : int id des Ziels in der Datenbank

Betrag : Dezimal (19,2 signiert) Preiswert positiv oder negativ bis summiert

account_balance : Dezimal (19,2 signiert) resultierender Saldo

extra_value_a : decimal (19,2 signiert) [dies war die vielseitigste Option ohne Verwendung des String-Speichers] Sie können eine zusätzliche Zahl speichern: Zinsprozentsatz, Rabatt, Ermäßigung usw.

created_at : Zeitstempel

Für den Quelltyp und den Zieltyp ist es besser, eine Aufzählung oder ein Tabellen-Appart zu verwenden.

Wenn Sie einen bestimmten Kontostand wünschen, können Sie einfach die letzte Operation abfragen, sortiert nach dem absteigenden Grenzwert von created_at auf 1. Sie können nach Quelle, Ziel, Operationstyp usw. abfragen.

Für eine bessere Leistung wird empfohlen, den aktuellen Kontostand im erforderlichen Zielobjekt zu speichern.

Heroselohim
quelle
2

Eine übliche Lösung für dieses Problem besteht darin, ein (etwa) monatliches Eröffnungsguthaben in einem Snapshot-Schema beizubehalten. Die Berechnung des aktuellen Kontostands kann durch Hinzufügen von Transaktionsdaten für den Monat zum monatlichen Eröffnungssaldo erfolgen. Dieser Ansatz wird häufig in Kontopaketen verwendet, insbesondere wenn Sie möglicherweise Währungsumrechnungen und Neubewertungen vornehmen.

Wenn Sie Probleme mit dem Datenvolumen haben, können Sie die älteren Salden archivieren.

Die Salden können auch für die Berichterstellung hilfreich sein, wenn Sie kein dediziertes externes Data Warehouse oder keine Management-Berichtsfunktion im System haben.

ConcernedOfTunbridgeWells
quelle
1

Natürlich müssen Sie Ihr aktuelles Guthaben in jeder Zeile speichern, sonst ist es zu langsam. Um die Entwicklung zu vereinfachen, können Sie Einschränkungen verwenden, sodass Sie keine Auslöser und regelmäßigen Überprüfungen der Datenintegrität benötigen. Ich habe es hier beschrieben. Denormalisieren, um Geschäftsregeln durchzusetzen: Ausführen von Summen

AK
quelle
2
Ich sehe, dass Sie Ihre Aktenablagesysteme mit Beton mögen.
PerformanceDBA
0

Ihr Freund liegt falsch und Sie haben Recht, und ich würde Ihnen raten, die Dinge jetzt nicht zu ändern.
Wenn Ihre Datenbank aus diesem Grund jemals langsam wird und Sie den Rest überprüft haben (ordnungsgemäße Indizierung), kann eine gewisse Denormalisierung hilfreich sein.
Sie können dann ein BalanceAtStartOfYear-Feld in die Konten-Tabelle einfügen und nur die diesjährigen Datensätze (oder einen ähnlichen Ansatz) zusammenfassen.
Aber ich würde diesen Ansatz sicherlich nicht im Voraus empfehlen.

Patrick Honorez
quelle
Ah ... nein, im Ernst. Die Buchhaltung ist ein heikles Geschäft, um mit möglicherweise einigen hohen gesetzlichen Anforderungen richtig umzugehen. Ad-hoc-Summierungen schneiden es nicht.
TomTom
1
@ TomTom. Natürlich tun sie dies, wenn die Transaktionen gemäß den Rechnungslegungsstandards implementiert sind (Ihre nicht). In diesem Fall ist "ad hoc" falsch. Außerdem ist das Duplizieren des CurrentBalance in jeder einzelnen Zeile einfach dumm: Wenn Anpassungen vorgenommen werden müssen, müssen Massen von Zeilen aktualisiert werden.
PerformanceDBA
0

Hier möchten wir Ihnen vorschlagen, wie Sie Ihr Eröffnungsguthaben auf sehr einfache Weise speichern können:

  1. Erstellen Sie eine Triggerfunktion für die Transaktionstabelle, die erst nach dem Aktualisieren oder Einfügen aufgerufen werden soll.

  2. Erstellen Sie eine Spalte mit Namen in der Haupttabelle des Kontonamens Eröffnungssaldo.

  3. Speichern Sie Ihren Eröffnungssaldo im Array in der Spalte Eröffnungssaldo in der Mastertabelle.

  4. Sie müssen nicht einmal die serverseitige Sprache verwenden. Verwenden Sie dieses Speicherarray. Sie können einfach Datenbankarrayfunktionen verwenden, wie sie in PostgreSQL verfügbar sind.

  5. Wenn Sie den Eröffnungssaldo im Array neu berechnen möchten, gruppieren Sie einfach Ihre Transaktionstabelle mit der Array-Funktion und aktualisieren Sie die gesamten Daten in der Mastertabelle.

Ich habe dies in PostgreSQL getan und funktioniert gut.

Während des Zeitraums, in dem Ihre Transaktionstabelle schwer wird, können Sie Ihre Transaktionstabelle anhand des Datums partitionieren, um die Leistung zu beschleunigen. Dieser Ansatz ist sehr einfach und es muss keine zusätzliche Tabelle verwendet werden, die die Leistung beim Verbinden von Tabellen beeinträchtigen kann, da eine geringere Tabelle beim Verbinden eine hohe Leistung bietet.

Ranjeet
quelle
Hallo, ich bin nicht klar mit Nr. 3. "Eröffnungssaldo" befindet sich in derselben Transaktionstabelle oder einer separaten Tabelle?
Axil
0

Mein Ansatz besteht darin, die Belastungen in einer Belastungsspalte und das Guthaben in der Guthabenspalte zu speichern und beim Abrufen der Daten zwei Arrays zu erstellen, das Belastungs- und das Guthabenarray. Hängen Sie dann die ausgewählten Daten weiter an das Array an und führen Sie dies für Python aus:

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

dann

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)
Kafeero
quelle
-3

Einfache Antwort: Mach alle drei.

Speichern Sie den aktuellen Kontostand. und in jeder Transaktion die Bewegung und eine Momentaufnahme des aktuellen Kontostands zu diesem Zeitpunkt speichern. Dies würde etwas Besonderes geben , das bei jeder Prüfung in Einklang gebracht werden kann.

Ich habe noch nie an Kernbankensystemen gearbeitet, aber ich habe an Anlageverwaltungssystemen gearbeitet, und meiner Erfahrung nach ist dies so.

Hundeohren
quelle
1
Dies gibt etwas Besonderes , das bei einem Audit in Einklang gebracht werden kann. Dies hilft bei der Prüfung der Software (im Hinblick auf korrekte Berechnungen und die Überprüfung, ob Transaktionen gelöscht werden; und ist nicht erforderlich = die einzige Möglichkeit, dies zu tun), wo ich arbeite und Teil einer IT-Prüfung ist. Dies steht jedoch nicht in direktem Zusammenhang mit der Rechnungsprüfung (die IT-Prüfung ist nur ein Teil der Finanzprüfung).
Unvernunft
Richtig, aber es ist nicht unwahrscheinlich, dass bei einem großen Interoperationssystem Situationen auftreten, in denen bestimmte Prozesse (z. B. eine Charge über Nacht) Anomalien verursachen. Ein bisschen Denormalisierung wird dazu beitragen, dass sie aufgespürt werden können.
Hundeohren
@ Dog Ears, ich verstehe - die meisten Ihrer Einträge stammen also aus dem Datenaustausch / der Interaktion, und dann ist die IT-Prüfung ein viel stärkerer Bestandteil der Finanzprüfung. Interessant. Trotzdem behalte ich meine Position: Dies ist ein zusätzlicher Kontrollwert, der für alle Transaktionen existieren würde. Wenn ich den Datenaustauschprozess prüfen wollte, würde ich Kontrollen einsetzen, die sich darauf konzentrieren (unter Verwendung einer Art Prüfsumme des Datenaustauschs: vom Initiator berechnet und einfach empfangen, berechnet auf den empfangenen Daten im empfangenen Format - prüft die Integrität der Übertragung , berechnet anhand der importierten Daten - überprüft Ihren Importvorgang)
Unreason
1
@smirkingman zum Wohle der Community Was ist schrecklich an meinem Rat, was unterscheidet ihn von deinem Rat? Scheint, dass wir im Grunde das gleiche vorgeschlagen haben?
Hundeohren
3
@ Hunde Ohren die Schrecklichkeit speichert das Gleichgewicht in jeder Bewegung. Mehr doppelte Daten und ein Albtraum, der behoben werden muss, wenn etwas schief geht.
Grinsender