Tabellenstrukturen für Haushaltsbuch gesucht
Melvin Cowznofski
- datenbank
0 Whouzuo0 dedlfix0 hotti
Hallo,
nachdem ich kürzlich meine Link-/Lesezeichensammlung erfolgreich auf eine Online Datenbank transferiert habe, will ich mich nun in meinem nächsten Projekt mit einem etwas komplexeren Thema beschäftigen: Ich möchte sowas wie ein Haushaltsbuch, eine privae Finanzverwaltung, basierend auf PHP und MySQL, verwirklichen.
Dazu habe ich mir überlegt, nach welcher Datenbank-/Verarbeitungslogik ich das anlege und welche Tabellen ich dazu benötige. Nach längerem Überlegen habe ich mich einstweilen für folgende Struktur entschlossen, zu der ich gerne Eure Meinung hören würde:
Für das komplette Projekt verwende ich insgesamt 7 Tabellen, welche folgendermaßen aufgebaut sind:
* ID ('1' oder '2')
* Cashflow ('Einnahme' oder 'Ausgabe')
* ID (zB. '3')
* Name (zB. 'Citybank Gehaltskonto', 'Hypo Bausparvertrag', ...)
* aktueller Kontostand (zB. '23.84')
* ID (zB. '21')
* Name (zB. 'Blumenhandlung HUBER', 'ALDI', 'HORNBACH Baumarkt', ...)
* ID (zB. '3')
* Buchungskategorie (zB. 'Lebensmittel', 'Theaterkarten', 'Gehalt', 'Weihnachtsgeschenk', ...)
* Parent ID (Für die Verwendung von Haupt- und Unterkategorien)
* ID (zB. '323')
* Datum (zB. '2013-11-25')
* Beteiligtes eigenes Konto (zB. '2')
* Beteiligter Finanzpartner (zB. '12')
* Cashflow (zB. '1')
* Gesamtbetrag (z. '231.12')
* ID (zB. '634')
* Transaktions ID (zB. '323')
* Cashflow (zB. '1')
* Buchungskategorie (zB. '12')
* Betrag (zB. '23.89')
* ID (zB. '232')
* Datum (zB. '2013-11-25')
* von eigenem Konto (zB. '2')
* nach eigenes Konto (zB. '3')
* Betrag (z. '100.00')
Das System dahinter: Jeder Transfer von meinem Geld an wen anderen oder jede Einnahme von außerhalb ist _eine_ Transaktion. Jede Transaktion besteht aus einer oder mehrere Buchungen. Das Datum der Zahlung sowie die Information, an wen gezahlt wurde bzw. woher Geld egkommen ist, steht deshalb in der Tabelle "Transaktionen". In der Tabelle "Buchungen", in der ja jeder einzelne Rechnungspunkt ein neuer Datensatz ist, stehen dann nur die Kategorie und der Teilbetrag, also kein Datum und kein Empfänger/Sender.
Als praktisches Beispiel: Angenommen, ich komme vom 'ALDI' nach Hause und habe einen Kassenbon, auf dem 3 Rechnungspunkte stehen. Also zB. 2 Bananan, 1 Pck. Milch und 1 Korb Weintrauben. Dann würde das in der Tabelle "Transaktionen" zu 1 neuen Eintrag führen und in der Tabelle "Buchungen" zu 3 Einträgen. Bei einer Transaktion, die aus einem einzigen Posten besteht, kommt es zu 1 neuen Eintrag bei den Transaktionen sowie 1 neuen Eintrag bei den Buchungen.
Nur die Cashflowart, also ob es sich um eine Einnahme oder Ausgabe handelt, existiert in _beiden_ Tabellen. Denn es kann ja sein, dass man zB. im Supermarkt Flaschenpfand zurückbekommt, dann stünde das als Einnahme auf der Rechnung. Trotzdem ist die Gesamtrechnung, also hier die Transaktion, eine Ausgabe.
Von Euch würde ich jetzt gerne wissen, wie Ihr das lösen würdet. Wie gesagt, mir geht es nur um den logischen Aufbau und die Tabellenstruktur, noch nicht um irgendwelche MySQL Befehle. Bei meinen Überlegungen hatte ich Modelle mit mehr und mit weniger Tabellen, aber sp wie hier vorgestellt finde ich es persönlich momentan am Besten. Jetzt wäre ich halt auf Eure Meinung dazu gespannt.
Mit lieben Grüßen
Melvin Cowznofski
Von Euch würde ich jetzt gerne wissen, wie Ihr das lösen würdet. Wie gesagt, mir geht es nur um den logischen Aufbau und die Tabellenstruktur, noch nicht um irgendwelche MySQL Befehle.
So wie es in der freien Wirtschaft schon seit langem gelöst ist: mit standardisierten Verfahren/Begriffen und doppelter Buchführung. Bei deiner Methode wirst du viele viele Probleme finden, die schon gelöst sind - du erfindest das Rad quasi neu. ;)
Hallo Whouzuo!
So wie es in der freien Wirtschaft schon seit langem gelöst ist:mit standardisierten Verfahren/Begriffen und doppelter Buchführung.
Und das heißt auf deutsch?
Bei deiner Methode wirst du viele viele Probleme finden
Und um welche Probleme handelt es sich da?
Mit lieben Grüßen
Melvin Cowznofski
Hallo Whouzuo!
So wie es in der freien Wirtschaft schon seit langem gelöst ist:mit standardisierten Verfahren/Begriffen und doppelter Buchführung.
Und das heißt auf deutsch?
Google einfach mal nach "doppelte Buchführung Datenbank".
Bei deiner Methode wirst du viele viele Probleme finden
Und um welche Probleme handelt es sich da?
Siehe bei Wikipedia "doppelte Buchführung" und folge den Links. Ansonsten google einfach. Dann findest du z.B: sowas: http://www.textbuch.de/tb/n007.html und noch viel mehr
Tach!
So wie es in der freien Wirtschaft schon seit langem gelöst ist:mit standardisierten Verfahren/Begriffen und doppelter Buchführung.
Ist das nicht ein bisschen ganz schön übertrieben? Ich meine, er will doch nur sehen, wofür das Geld ausgegeben wird und kein Geschäft führen, bei dem am Ende steuerlich und anderswie relevante Nachweise entstehen müssen.
Bei deiner Methode wirst du viele viele Probleme finden
Und um welche Probleme handelt es sich da?
Siehe bei Wikipedia "doppelte Buchführung" und folge den Links. Ansonsten google einfach. Dann findest du z.B: sowas: http://www.textbuch.de/tb/n007.html und noch viel mehr
Und wo ist da der konkrete Vorteil für die „Taschengeld“verwaltung? Oder der Nachteil, wenn man selbiges nicht doppelt buchführt?
dedlfix.
Tach!
1.) Tabelle "Cashflows"
* ID ('1' oder '2')
* Cashflow ('Einnahme' oder 'Ausgabe')
Die Tabelle ist unnötig, dafür gibt es Vorzeichen, + rein, - raus.
2.) Tabelle "Eigene Konten"
* ID (zB. '3')
* Name (zB. 'Citybank Gehaltskonto', 'Hypo Bausparvertrag', ...)
* aktueller Kontostand (zB. '23.84')
Den Kontostand extra zu führen ist nicht notwendig, der ergibt sich aus der Summe der Buchungsbeträge (mit ihren Vorzeichen). Das hat sogar den Vorteil, dass Fehler auffallen. Dann stimmt nämlich die selbst berechnete Summe nicht mit dem Kontostand überein.
Eventuell möchtest du noch eine Spalte Kontotyp einführen, damit du die Konten zum Beispiel in Zusammenfassungen nach Arten gruppieren kannst. Girokonten betrachtet man anders als Sparkonten. Ein Kredit wäre auch ein Konto, bei dem man aber Tilgungen berechnen kann, was für Girokonten unsinnig wäre. Aber das später zu implementieren, wenn dir die Anwendungsfälle dazu einfallen, wäre auch kein Beinbruch.
3.) Tabelle "Finanzpartner"
4.) Tabelle "Buchungskategorien"
Passt.
5.) Tabelle "Transaktionen"
* ID (zB. '323')
* Datum (zB. '2013-11-25')
* Beteiligtes eigenes Konto (zB. '2')
* Beteiligter Finanzpartner (zB. '12')
* Cashflow (zB. '1')
* Gesamtbetrag (z. '231.12')
Cashflow weg, ergibt sich aus dem Vorzeichen.
6.) Tabelle "Buchungen"
* ID (zB. '634')
* Transaktions ID (zB. '323')
* Cashflow (zB. '1')
* Buchungskategorie (zB. '12')
* Betrag (zB. '23.89')
Das hab ich bei mir nicht extra sondern in der Transaktionentabelle drin. Die sieht so aus:
CREATE TABLE Transaction
(
ID
int(10) unsigned NOT NULL AUTO_INCREMENT,
ID\_Account
int(10) unsigned DEFAULT NULL,
Date
date NOT NULL DEFAULT '0000-00-00',
Sort
tinyint(3) unsigned DEFAULT NULL,
Amount
decimal(20,2) NOT NULL,
ID\_Payee
int(10) unsigned DEFAULT NULL,
ID\_Category
int(10) unsigned DEFAULT NULL,
Memo
varchar(255) NOT NULL DEFAULT '',
Type
set('Transfer','Split','SplitPart') DEFAULT NULL,
Flag
enum('','A','V') NOT NULL DEFAULT '',
ReferenceID
int(10) unsigned DEFAULT NULL,
PRIMARY KEY (ID
),
KEY ID\_Account
(ID\_Account
),
KEY ID\_Payee
(ID\_Payee
),
KEY ID\_Category
(ID\_Category
),
KEY ReferenceID
(ReferenceID
),
KEY Date
(Date
)
);
Sort bekommt bei Bedarf eine Nummer, um Buchungen (=Transaktionen) eines Tages definiert anordnen zu können. Das ist nur Optik und kann auch wegbleiben. Memo ist ein Freitextfeld, wenn ich was zur Buchung zu vermerken habe (z.B was für ein Ding oder welche Vertragsnummer sie betrifft). Flag definiert ob verrechnet oder abgestimmt. Abgestimmt bedeutet, dass die Buchung mit einem Beleg verglichen wurde (Kontoauszug vs. Rechnung/Kassenzettel). Wenn es naturgemäß keinen Beleg gibt (wie bei Daueraufträgen), dann bekommt die Buchung trotzdem ihr A. Verrechnet ist eine Buchung, wenn sie auf dem Kontoauszug auftaucht, aber (noch) nicht mit einem Beleg vergleichen wurde, weil der zum Beispiel noch nicht da ist. Wenn das Feld leer ist, dann ist vielleicht schon der Beleg da oder auch nicht, jedenfalls ist das noch nicht auf dem Kontoauszug erschienen. Ich hab's aber trotzdem schon eingetragen, um es bei Planungen berücksichtigen zu können. Der Idealzustand ist also A. Diese Kennzeichnung gab es damals in MS-Money, und vielleicht ist ihre eigentliche Bedeutung auch eine ganz andere, ich hab es jedenfalls so interpretiert.
Type hat drei Aufgaben, abgesehen von NULL, was eine einfache Buchung darstellt. Transfer ist eine Überweisung zwischen zwei Konten. Das Feld ReferenceID bekommt dabei die Nummer der Gegenbuchung auf dem anderen Konto zugewiesen (was in drei Schritten erfolgt: erste Buchung anlegen, zweite Buchung mit Referenz auf die ID der ersten anlegen, Updaten der Referenz der ersten mit der ID der zweiten). Split sind Hauptbuchungen, wie beim Lohn/Gehalt oder Rechnungen, die ich auf Kategorien aufteilen möchte. SplitPart sind die einzelnen Teile, wobei da die ReferenceID auf die zugehörige Split-Buchung verweist. Idealerweise ergibt die Summe der SplitPart-Beträge denselben Wert wie die zugehörige Split-Buchung, aber das muss nicht so sein, wenn man beispielsweise unwichtige Teile nicht weiter aufdröseln möchte. Jedenfalls darf die Summe des/der Kontos nur ohne die SplitPart-Buchungen berechnet werden.
7.) Tabelle "Interne Umbuchungen"
Nicht notwendig, das sind zwei Einzelbuchungen auf den jeweiligen Konten, bei mir durch den „Transfer“-Type gekennzeichnet.
Als praktisches Beispiel: Angenommen, ich komme vom 'ALDI' nach Hause und habe einen Kassenbon, auf dem 3 Rechnungspunkte stehen. Also zB. 2 Bananan, 1 Pck. Milch und 1 Korb Weintrauben. Dann würde das in der Tabelle "Transaktionen" zu 1 neuen Eintrag führen und in der Tabelle "Buchungen" zu 3 Einträgen. Bei einer Transaktion, die aus einem einzigen Posten besteht, kommt es zu 1 neuen Eintrag bei den Transaktionen sowie 1 neuen Eintrag bei den Buchungen.
Das wäre mir viel zu zeitaufwendig ohne weiteren Nutzen. Kategorie Lebensmittel für den gesamten Einkauf und gut ist. Eventuell kann man das splitten, wenn was besonderes dabei war, dann aber in „besonderes Ding“ und „Rest“.
Beachte auch, als Spaltentyp für die Beträge DECIMAL und nicht FLOAT zu verwenden, sonst gibt es den bekannten und befürchteten Ärger mit ungenauer Abbildbarkeit von Nachkommastellen im Dualsystem (à la 1 - 0.1 = 0.89999999) .
dedlfix.
Hallo dedlfix!
Danke für diese ausführliche Antwort!
Die Tabelle ist unnötig, dafür gibt es Vorzeichen, + rein, - raus.
Du meinst also, Ausagben sind dadurch gekennzeichnet, dass es sich beim Betrag um eine negative Zahl handelt. Meine Überlegung war, dass ich mir dann zB. alle Ausgaben einer bestimmten Kategorie in einem bestimmten Zeitraum ausgeben lassen kann. SELECT ... WHERE cashflow = 1
. Aber natürlich erreiche ich das auch mit einem SELECT ... WHERE betrag < 0
. War das so gemeint von Dir?
Den Kontostand extra zu führen ist nicht notwendig, der ergibt sich aus der Summe der Buchungsbeträge (mit ihren Vorzeichen).
Da stocke ich jetzt etwas mit meinem Verständnis. Wenn ich soetwas wie eine Start-Übersichtsseite mit den Kontoständen all meiner Konten haben möchte, dann ist das mit der "Kontostand"-Spalte eine Abfrage eines einzigen Feldes. Ansonsten wäre ich gezwungen, bei 5 Konten 5 mal die komplette Datenbank durchlaufen zu lassen und mir sämtliche Buchungswerte zusammenzählen zu lassen. Das ist doch über-performancelastig und kompliziert. Oder denkst Du da an einen völlig anderen Weg, den aktuellen Stand ausgeben zu lassen ohne einer eigenen
"Kontostand"-Spalte?
Das hat sogar den Vorteil, dass Fehler auffallen. Dann stimmt nämlich die selbst berechnete Summe nicht mit dem Kontostand überein.
Ich würde ja den Kontostand nicht manuell eintragen. Bei der Speicherung einer neuen Transaktion würde ich einfach den aktuellen Kontostand aufrufen, entsprechend summieren oder subtrahieren und den neuen Wert wieder speichern. Da kann es nicht zu Fehlern kommen. Also noch fehlt mir das verständnis, wieso die "Kontostand"-Spalte nicht gut ist. Ich bitte um weitere Aufklärung und Hilfestellung!
Eventuell möchtest du noch eine Spalte Kontotyp einführen, damit du die Konten zum Beispiel in Zusammenfassungen nach Arten gruppieren kannst.
Dazu sehe ich bei _meinen_ Bedürfnissen keinen Anlass. Meine Konten wären bei 2 verschiedenen Banken jeweils 1 Girokonto und 1 Sparkonto, das Guthaben auf den Quick-Chips der Kundenkarten, dazu ein weiteres Sparkonto bei einer Onlinebank, meine Prepaid Kreditkarte, mein Paypalkonto sowie das Bargeld. Für eine Unterscheidung zwischen den Sparkonten und den Girokonten sehe ich keinen Grund. Beides sind Orte für Geldein- und Geldausgänge. Aber wenn ich Dich richtig verstehe, ging es Dir darum, dass ich sowas brauche, wenn ich zB. das Guthaben aller Sparkonten sehen möchte. Dann wäre evt. auch noch ein "available" Feld interessant. 1 für verfügbares Geld, 0 für nicht verfügbares Geld, wie zB. bei einem gesperrten Sparbuch oder einem Bausparvertrag. Also wenn ich Dich in diesem Punkt richtig verstanden habe, werde ich das mal im Hinterkopf behalten.
3.) Tabelle "Finanzpartner"
4.) Tabelle "Buchungskategorien"
Passt.
Da kommt Freude auf, wenn zumindest _irgendwas_ in meinen Überlegungen fehlerfrei ist! =)
Das hab ich bei mir nicht extra sondern in der Transaktionentabelle drin. Die sieht so aus:
Die von Dir gepostete Tabellenerstellung habe ich bei mir lokal via phpMyAdmin laufen lassen und mir die entstandene Tabelle genauer angesehen. Nach längerer Zeit denke ich, dass ich Dein System zum größten Teil verstanden habe. Aber nur, um das abzuckecken:
Deine Spalte "ID_Account" bezeichnet die ID des Kontos, das bei dieser Transaktion beteiligt ist. Festgelegt durch die IDs in der Tabelle "Accounts" (oder wie immer auch bei Dir bezeichnet.) Richtig?
Und ich nehme an, Deine Spalte "ID_Payee" bezeichnet bei Dir das, was ich als "Finanzpartner" definiert habe, also die gegenseite der Finanztransaktion. Richtig?
Der Idealzustand ist also A. Diese Kennzeichnung gab es damals in MS-Money, und vielleicht ist ihre eigentliche Bedeutung auch eine ganz andere, ich hab es jedenfalls so interpretiert.
Ja, es hat eine andere Bedeutung. Dieses Zeichen wird nur fälschlicherweise für ein A gehalten. In Wirklichkeit ist der obere Teil des Zeichens eine Pyramide und zeigt die Verbindung zu Freimaurern und Illuminaten. In Verbindung mit den beiden "Füßen" am unteren Rand der Pyramide entsteht das skizzierte Bild der angeblichen Mondlandekapsel "Eagle". Aber wie wir alle wissen, war die Mondlandung eine Lüge.
Doch zurück zu Deiner Antwort: Interessant finde ich Deine Idee mit der "ReferenceID" Spalte, die im Falle einer Splitbuchung den Bezug zur "Gesamtrechnung" darstellt und im Falle einer internen Umbuchung die Buchungs-ID der Gegenseite zeigt.
Was ich am wenigsten verstehe, ist die Spalte "Sort":
Sort bekommt bei Bedarf eine Nummer, um Buchungen (=Transaktionen) eines Tages definiert anordnen zu können. Das ist nur Optik und kann auch wegbleiben.
Dazu kann ich mir absolut nichts vorstellen. Meinst Du, dass da temporär etwas in dieser Spalte gespeichert wird und nach einer Berechnung/Ausgabe wieder gelöscht wird? Kannst Du das praktisch irgendwie erläutern?
Also was ich mir auf jeden Fall schon mal mitnehme von Deiner Antwort:
Ich werde komplett auf die Tabelle "Cashflow" verzichten und Einnahmen/Ausgaben anhand der positiven/negativen Zahl im "Betrag" Feld identifizieren.
Ich werde komplett auf die Tabelle "Interne Umbuchungen" verzichten und das so wie Du lösen. Dazu habe ich mir auch schon Gedanken gemacht. Ich arbeite ja mit PDO als Verbindung zwischen php und MySQL. Mit $id = $db -> lastInsertId()
bekomme ich nach dem Speichern der ersten Buchung die ID, verwende diese dann als Wert für die ReferenceID bei der Speicherung der 2. Buchung, bekomme hier wiederum mit $id = $db -> lastInsertId()
zur ID und mit einem SET ... WHERE ID = ...
wird dieser Wert dann noch bei der ersten Buchung als ReferenceID nachgetragen. Das gefällt mir!
Wovon Du mich nicht überzeugt hast, ist Dein Zusammenschluss von Einzelbuchungen und Splitbuchungen. Für mich ist das einfach nicht das Selbe. Wenn ich beim Aldi 1 Banane, 1 Joghurt und 1 Wurst kaufe, dann bezahle ich ja an der Kasse nicht 3 Mal hintereinander jedes einzelne Produkt extra. Es gibt _einen_ Bezahlvorgang. Das, was ich als Transaktion bezeichne. Diese Transaktion hat einen Empfänger, ein Datum und einen Gesamtbetrag. Um welche Produkte es sich dabei genau gehandelt hat und was die Preise dieser Einzelprodukte waren, ist wieder eine andere Sache. Dafür die Buchungstabelle, die die Splitrechnungen aufzeigt. Hier wiederum ist für mich der Empfänger und das Datum uninteressant, da durch die ReferenceID der Bezug zur Gesamtrechnung ja gegeben ist. Ich kann mich einfach nicht damit anfreunden, das in eine einzige Tabelle zu stecken. Nicht, weil es mir technisch zu kompliziert wäre oder ich Dein Konzept nicht verstehe, sondern weil ich einfach 2 verschiedene Dinge darin sehe und mich dagegen sträube, die in einen Topf zu werfen. Findest Du das wirklich _so_ schlimm/falsch?
Recht hast Du natürlich damit, dass die Gesamtvermögensberechnungen auf Grund der Transaktionen, also der Hauptrechnungen durchgeführt werden.
Das wäre mir viel zu zeitaufwendig ohne weiteren Nutzen. Kategorie Lebensmittel für den gesamten Einkauf und gut ist. Eventuell kann man das splitten, wenn was besonderes dabei war, dann aber in „besonderes Ding“ und „Rest“.
Nun ja, in diesem Punkt bin ich wohl ein kleiner Erbsenzähler. :)
Beachte auch, als Spaltentyp für die Beträge DECIMAL und nicht FLOAT zu verwenden, sonst gibt es den bekannten und befürchteten Ärger mit ungenauer Abbildbarkeit von Nachkommastellen im Dualsystem (à la 1 - 0.1 = 0.89999999).
Ich weiß jetzt nicht genau, was Du damit meinst, aber ich habe hier beim Mitlesen im Forum schon ein paar Mal mitbekommen, dass es bei Verwendung von FLOAT Probleme gibt. Wenn ich mich richtig erinnere, auch ber der Berechnung von Mittelwerten oder Ähnlichem. Danke für den Hinweis, dann verwende ich DECIMAL! (Nur so nebenbei: Wenn es mit FLOAT so viele Fälle gibt, die Probleme machen, wann verwendet man es dann denn auf jeden Fall _statt_ dem DECIMAL? Irgend eine Daseinsberechtigung muss es ja haben?!)
Was jetzt also als Frage offen bleibt ist die Sache mit dem Weglassen der Spalte für den aktuellen Kontostand und die Sache mit dem Sort.
Mit lieben Grüßen
Melvin Cowznofski
Tach!
Du meinst also, Ausagben sind dadurch gekennzeichnet, dass es sich beim Betrag um eine negative Zahl handelt. Meine Überlegung war, dass ich mir dann zB. alle Ausgaben einer bestimmten Kategorie in einem bestimmten Zeitraum ausgeben lassen kann.
SELECT ... WHERE cashflow = 1
. Aber natürlich erreiche ich das auch mit einemSELECT ... WHERE betrag < 0
. War das so gemeint von Dir?
Exaktamente.
Den Kontostand extra zu führen ist nicht notwendig, der ergibt sich aus der Summe der Buchungsbeträge (mit ihren Vorzeichen). Da stocke ich jetzt etwas mit meinem Verständnis. Wenn ich soetwas wie eine Start-Übersichtsseite mit den Kontoständen all meiner Konten haben möchte, dann ist das mit der "Kontostand"-Spalte eine Abfrage eines einzigen Feldes. Ansonsten wäre ich gezwungen, bei 5 Konten 5 mal die komplette Datenbank durchlaufen zu lassen und mir sämtliche Buchungswerte zusammenzählen zu lassen.
SELECT SUM(Amount) FROM Transactions GROUP BY ID_Account;
Dazu noch die Account-Tabelle joinen für den Namen des Kontos und in meinem Datenmodell die Splitbuchungen ausklammern.
Das ist doch über-performancelastig und kompliziert. Oder denkst Du da an einen völlig anderen Weg, den aktuellen Stand ausgeben zu lassen ohne einer eigenen "Kontostand"-Spalte?
Bei 15000 Transaktionsdatensätzen kann ich da keine Verzögerung spüren.
Das hat sogar den Vorteil, dass Fehler auffallen. Dann stimmt nämlich die selbst berechnete Summe nicht mit dem Kontostand überein. Ich würde ja den Kontostand nicht manuell eintragen. Bei der Speicherung einer neuen Transaktion würde ich einfach den aktuellen Kontostand aufrufen, entsprechend summieren oder subtrahieren und den neuen Wert wieder speichern. Da kann es nicht zu Fehlern kommen. Also noch fehlt mir das verständnis, wieso die "Kontostand"-Spalte nicht gut ist. Ich bitte um weitere Aufklärung und Hilfestellung!
Nimm den Fall, dass du gerade alle Buchungen vom letzten Kontoauszug eingetragen hast. Dabei hast du dich bei einem Betrag vertippt (Vorzeichen oder Wert). Das merkst du bei der (Zwischen)Summenbildung nicht, sondern erst nach Abschluss aller Einträge, weil du auf dem Kontoauszug auch keine Zwischensummen sondern nur den alten und neuen Stand stehen hast. Jetzt korrigierst du die eine Transaktion/Buchung und musst dann noch mit dem Differenzbetrag zwischen fehlerhafter und korrigierter Buchung/Transaktion die Kontosumme ausgleichen. Das ist nun nicht gerade ein Akt, aber trotzdem zusätzlicher Code statt einfach nur einer einfachen Datensatzänderung.
Das hab ich bei mir nicht extra sondern in der Transaktionentabelle drin. Die sieht so aus: Die von Dir gepostete Tabellenerstellung habe ich bei mir lokal via phpMyAdmin laufen lassen und mir die entstandene Tabelle genauer angesehen. Nach längerer Zeit denke ich, dass ich Dein System zum größten Teil verstanden habe. Aber nur, um das abzuckecken:
Deine Spalte "ID_Account" bezeichnet die ID des Kontos, das bei dieser Transaktion beteiligt ist. Festgelegt durch die IDs in der Tabelle "Accounts" (oder wie immer auch bei Dir bezeichnet.) Richtig? Und ich nehme an, Deine Spalte "ID_Payee" bezeichnet bei Dir das, was ich als "Finanzpartner" definiert habe, also die gegenseite der Finanztransaktion. Richtig?
Beides richtig.
Was ich am wenigsten verstehe, ist die Spalte "Sort":
Sort bekommt bei Bedarf eine Nummer, um Buchungen (=Transaktionen) eines Tages definiert anordnen zu können. Das ist nur Optik und kann auch wegbleiben. Dazu kann ich mir absolut nichts vorstellen. Meinst Du, dass da temporär etwas in dieser Spalte gespeichert wird und nach einer Berechnung/Ausgabe wieder gelöscht wird? Kannst Du das praktisch irgendwie erläutern?
Per Definition liegen die Datensätze in einer Tabelle in einer beliebigen Reihenfolge, selbst wenn das DBMS praktisch die Reihenfolge in der physischen Ordnung nimmt. Man sortiert also nach Datum und bekommt alle Datensätze vom selben Tag theoretisch in einer beliebigen Reihenfolge angezeigt. Das kann einem egal sein, aber wenn man gern die Reihenfolge wie auf dem Kontoauszug haben möchte, dann reicht das Datum allein nicht. Eine gefakte Uhrzeit hinzuzufügen ist nicht die beste Idee. Außerdem kann es vorkommen, dass ich eine Buchung einzutippen übersehe und sie erst nach ein paar anderen nachtrage. Die steht dann zwar zu dem Datum aber irgendwo am Ende, der Buchungen dieses Tages. Die Alternativen, die Danach-Datensätze zu löschen und alle in der richtigen Reihenfolge wieder einzugeben oder gar IDs umzuschreiben oder eine physische Umsortierung, sind keine. Wenn ich also Wert auf eine bestimmte Reihenfolge innerhalb eines Tages lege, dann brauch ich ein weiteres Sortierkriterium. Und das gibt mir die Sort-Spalte. »ORDER BY Date, Sort« Es ist wesentlich einfacher bei ein paar Datensätzen ein paar Zahlen in ein Feld einzutragen oder dort ein paar Änderungen vorzunehmen. Sie müssen ja auch nur relativ zum Tag vergeben werden (oder zur Split-Buchung). Meist bleibt Sort leer, wenn es nur eine Buchung am Tag ist oder eine weitere Sortierung mir nicht notwendig erscheint. Alles in allem ist Sort also ein Kriterium zu einer feineren Sortierung. Und vermutlich entspricht das auch gar nicht buchhalterischen Kriterien (besonders die erwähnten Korrekturen beim Vertippen), aber ich halte ja kein Buch sondern nur die Übersicht in meinen privaten Finanzen auf einem für mich brauchbaren Niveau.
Ich werde komplett auf die Tabelle "Interne Umbuchungen" verzichten und das so wie Du lösen. Dazu habe ich mir auch schon Gedanken gemacht. [...] Mit
$id = $db -> lastInsertId()
bekomme ich nach dem Speichern der ersten Buchung die ID, verwende diese dann als Wert für die ReferenceID bei der Speicherung der 2. Buchung, bekomme hier wiederum mit$id = $db -> lastInsertId()
zur ID und mit einemSET ... WHERE ID = ...
wird dieser Wert dann noch bei der ersten Buchung als ReferenceID nachgetragen. Das gefällt mir!
Mir nicht so sehr, aber unter den gegebenen Umständen lässt sich das nicht viel anders machen. Wenn man im Fehlerfall keine halben Sachen im DBMS stehen haben will, sollte man den Vorgang (erster Eintrag, zweiter Eintrag, zweite ID beim ersten nachtragen) zumindest als eine Transaction (im DBMS-Sinne, nicht im finanziellen) ausführen.
Andere DBMSe haben kein Auto-Increment, dessen Wert erst nachher zur Vefügung steht. Die haben dafür Sequenzen. Das sind unabhänig laufende Zahlenhochzähler (und ebenfalls session- und nebenläufigkeitsproblemfrei). Die befragt man vor dem Insert nach zwei neuen Werten und macht die beiden Inserts gleich mit den richtigen Werten. Oder man erzeugt eindeutige IDs gleich ganz selbst (GUID/UUID).
Wovon Du mich nicht überzeugt hast, ist Dein Zusammenschluss von Einzelbuchungen und Splitbuchungen. Für mich ist das einfach nicht das Selbe. Wenn ich beim Aldi 1 Banane, 1 Joghurt und 1 Wurst kaufe, dann bezahle ich ja an der Kasse nicht 3 Mal hintereinander jedes einzelne Produkt extra. Es gibt einen Bezahlvorgang. Das, was ich als Transaktion bezeichne. Diese Transaktion hat einen Empfänger, ein Datum und einen Gesamtbetrag. Um welche Produkte es sich dabei genau gehandelt hat und was die Preise dieser Einzelprodukte waren, ist wieder eine andere Sache. Dafür die Buchungstabelle, die die Splitrechnungen aufzeigt. Hier wiederum ist für mich der Empfänger und das Datum uninteressant, da durch die ReferenceID der Bezug zur Gesamtrechnung ja gegeben ist. Ich kann mich einfach nicht damit anfreunden, das in eine einzige Tabelle zu stecken. Nicht, weil es mir technisch zu kompliziert wäre oder ich Dein Konzept nicht verstehe, sondern weil ich einfach 2 verschiedene Dinge darin sehe und mich dagegen sträube, die in einen Topf zu werfen. Findest Du das wirklich so schlimm/falsch?
Deine Tabellenaufteilung geht anders an die Sache ran. Weil es eine Kategorisierung nur in den Buchungen, nicht aber bei den Transaktionen gibt, gehe ich davon aus, dass zu jeder Transaktion mindestens ein Buchungsdatensatz angelegt wird (oder mehrere wenn es sich um eine Splitbuchung handelt). In dem steht dann kategorisiert und/oder als Text (Feld dafür fehlt noch) was du gekauft hast. Das kann man so machen und ist genauer betrachtet vermutlich sogar besser als mein System. Ich nehme an, ich hab bei meinem Modell zuerst in Einzelbuchungen gedacht und dann den Split-Teil hinzugefügt. Das System entstand ja auch schon vor vielen Jahren und Erfahrungen.
Jetzt muss ich nochmal nachdenken, wie Umbuchungen in das getrennte System zu integrieren wären. Die ReferenzId ist für Splitbuchungen überflüssig, weil die Zuordnung bereits über die Transaktionsfremdschlüssel in den Buchungen gegeben ist. Das heißt, sie wird nur noch für den nicht so häufigen Fall der Überweisungen zwischen zwei Konten benötigt. Als Alternative zur zusätzlichen, meist leeren Spalte fällt mir da grad nur eine Zuordnungstabelle ein. Aber letztere wirft weitere Fragen auf. a und b seien zwei zusammenhängende Transaktionen. Macht man nun nur a-b-Datensätze, die auch b-a-Datensätze sein können, je nachdem von welcher Transaktionsseite man schaut? Oder macht man jeweils zwei Datensätze, einmal a-b und einmal b-a? Das ist redundant, aber der Vorteil ergibt sich beim Suchen. Da gibt es nämlich das Problem, dass man von einer Transaktion ausgehend in beiden Spalten nachsehen muss, ob deren ID eingetragen ist und dann in der jeweils anderen den Partner findet. Man weiß es ja nicht, ob man den a- oder den b-Teil der Umbuchung vorliegen hat. Wobei, hmm, das Vorzeichen des Betrages könnte ein Hinweis sein. Das wiederum steht aber nur bei den Buchungen (siehe etwas weiter unten). - Vermutlich ist es doch einfacher, eine zusätzliche ReferenceID-Spalte zu nehmen, auch wenn man da das AutoIncrement-ID-Problem hat.
Recht hast Du natürlich damit, dass die Gesamtvermögensberechnungen auf Grund der Transaktionen, also der Hauptrechnungen durchgeführt werden.
Mir ist erst in dieser Antwort klar geworden, wie du die Transaktionen und Buchungsdaten aufteilen willst. (Ich würde es sicher nicht als Buchung (das ist ja quasi synonym zu Transaktion) sondern als Posten oder Details, auf jeden Fall aber englisch benennen.) Es kann jedenfalls sein, dass ich dir nun Vorschläge mache, die anders sind als die vorherigen.
Was du bei deiner Aufteilung nicht brauchst, ist eine Betragsspalte bei den Transaktionen. Die wäre nur doppelt und bringt keinen wirklichen Vorteil gegenüber einem SUM() der zugehörigen Buchungen (über Gruppierung oder Correlated Subquery berechnet).
Beachte auch, als Spaltentyp für die Beträge DECIMAL und nicht FLOAT zu verwenden, sonst gibt es den bekannten und befürchteten Ärger mit ungenauer Abbildbarkeit von Nachkommastellen im Dualsystem (à la 1 - 0.1 = 0.89999999).
Ich weiß jetzt nicht genau, was Du damit meinst, aber ich habe hier beim Mitlesen im Forum schon ein paar Mal mitbekommen, dass es bei Verwendung von FLOAT Probleme gibt. Wenn ich mich richtig erinnere, auch ber der Berechnung von Mittelwerten oder Ähnlichem. Danke für den Hinweis, dann verwende ich DECIMAL! (Nur so nebenbei: Wenn es mit FLOAT so viele Fälle gibt, die Probleme machen, wann verwendet man es dann denn auf jeden Fall statt dem DECIMAL? Irgend eine Daseinsberechtigung muss es ja haben?!)
Die genauen Unterschiede stehen sicher irgendwo zum Nachlesen (Wikipedia oder ähnlich). Float ist besser an das Dualsystem angepasst. Das Dezimalsystem auf einem Dualsystem abzubilden verschenkt Platz und passt nicht direkt in die Rechnerarchitektur. Meist ist es nicht wichtig, wie genau eine Zahl an der drölften Stelle ist, nur der Anfang und der Exponent für die Größenordnung zählt. Deshalb sind die Ungenauigkeiten von Float kein Beinbruch. Beim Geld aber hört die Freundschaft auf und da muss es präzise zugehen, auch wenn die Dualsystemprozessoren dabei mehr Ressourcen verbrauchen.
dedlfix.
hi,
guck Dir einen ganz normalen Kontoauszug an: Grundgerüst für eine Tabelle.
Felder für ein Konto: soll, haben, saldo, datum, freitext
Das System dahinter: Jeder Transfer von meinem Geld an wen anderen oder jede Einnahme von außerhalb ist _eine_ Transaktion. Jede Transaktion besteht aus einer oder mehrere Buchungen. Das Datum der Zahlung sowie die Information, an wen gezahlt wurde bzw. woher Geld egkommen ist, steht deshalb in der Tabelle "Transaktionen". In der Tabelle "Buchungen", in der ja jeder einzelne Rechnungspunkt ein neuer Datensatz ist, stehen dann nur die Kategorie und der Teilbetrag, also kein Datum und kein Empfänger/Sender.
Das Transaktionskonzept ist dann wichtig, wenn außer Dir selbst noch andere Prozesse in der Tabelle buchen.
Als praktisches Beispiel: Angenommen, ich komme vom 'ALDI' nach Hause und habe einen Kassenbon, auf dem 3 Rechnungspunkte stehen. Also zB. 2 Bananan, 1 Pck. Milch und 1 Korb Weintrauben. Dann würde das in der Tabelle "Transaktionen" zu 1 neuen Eintrag führen und in der Tabelle "Buchungen" zu 3 Einträgen. Bei einer Transaktion, die aus einem einzigen Posten besteht, kommt es zu 1 neuen Eintrag bei den Transaktionen sowie 1 neuen Eintrag bei den Buchungen.
Deinem Verständnis zum Begriff 'Transaktion' entsprechend würde ich einfach nur triggern.
Nur die Cashflowart, also ob es sich um eine Einnahme oder Ausgabe handelt, existiert in _beiden_ Tabellen.
Das geht in einer Tab: Soll oder Haben.
Horst Zahlmeister