# Aufbau meiner Datenbank/Tabelle - Verbessern? So lassen?



## jhjh (7. Feb 2019)

Hallo,
in meiner Tabelle habe ich u.A. 3 Spalten:
*Zahlungsart *->  [Barzahler, Rechnungszahler] 
(heißt: Wert kann entweder Barzahler o. Rechungszahler sein
*Zahlungsintervall *-> [Wöchentlich,Monatlich,Monatsanfang,Monatsende,null]
*Zahlungszeitpunkt *-> [Monatsanfang,Monatsende,null]
Ich speichere hier momentar als VARCHAR

Die 3 Spalten hängern wie folgt miteinander zusammen:
*
Zahlungsart->   Zahlungsintervall->   Zahlungszeitpunkt*
Rechnungszahler->   null->   null
Barzahler->   Wöchentlich->   null
Barzahler->   Monatlich   ->   [Monatsanfang,Monatsende]
Barzahler->   2-Monatlich->   [Monatsanfang,Monatsende]
Barzahler->   3-Monatlich->   [Monatsanfang, Monatsende]

So wie in der Tabelle speicher ich die Daten auch in meine Kundentabelle 
(Bei [Monatsanfang,Monatsende] dann halt entweder Monatsanfang oder Monatsende)
Jetzt frage ich mich ob ich das so lassen soll, oder ob ich es villeicht lieber abändern sollte und weshalb. 
Meine Überlegung:
Hinsichtlich der Art der Speicherung könnte man ich statt Zahlungsart beispielsweise Rechnungszahler nehmen und dann über den Datentyp BOOLEAN einfach nur _true _oder _false_ speichern. Bei Zahlungsintervall könnte ich zB INT nehmen (1 für Wöchentlich, 2 für Monatlich etc).  Bei Zahlungszeitpunkt würde sich dann wieder BOOLEAN anbieten. 
Vorteil wäre dann hier wohl, dass ich weniger Fehleranfällig wäre, aber sonst ? Gäbe es da noch einen Vorteil ? Wie würdet Ihr das handhaben ? Zusätliche Tabelle ? 

Zusätzlich habe ich noch ähnliche Entweder-Oder Attribute. So habe ich beispielsweise die Attribute Kreis (INT) und Polygon (VARCHAR). Der Kunde kann entweder einen Kreis oder einen Polygon haben. Wenn der Kunde einen Kreis besitzt, dann kann dieser unterschiedliche Werte einnehmen (2 , 3 .... 200 ....). Zusätzlich wird Polygon dann auf *null *gesetzt. Wenn der Kunde einen Polygon hat, wird halt der Kreis auf *null *gesetzt. 
Auch hier die Frage ? Würdet ihr das ähnlich machen ? Falls nein ? Warum nicht ?

Meine Tabelle ist in etwa wie folgt aufgebaut
KnId -> Nname ->Vname -> Straße -> Stadt -> Radius -> Polygon ->Zahlungsart -> Zahlungsintervall -> Zahlungszeitpunkt 
(Gibt zwar noch mehr Spalten, aber die sind nicht von Bedeutung, da sie ähnlich wie Nname und Vname keine Abhängigkeiten untereinander haben)

Ich bedanke mich! =)


----------



## Thallius (7. Feb 2019)

Also,

wenn Du eine Tabelle mit den möglichen Werten für die Zahlungsart hast, wozu hast du diese dann noch einmal in den Kunden drin? Die Tabelle mit den Zahlungsarten sollte eine unique ID haben (einfach den primary key autoincrement nehmen). Beim Kunden steht dann nur die ID zur entsprechenden Zahlungsart. Das gleiche gilt für die Objekte. Du hast eine Tabeller mit Objekten (Kreis, Polygon was auch immer). Jedes Object hat eine unqiue ID. In der Kundentabelle steht dann nur die ID des Objektes. Kann der Kunde mehrere Objekte haben, dann erstellt man eine zusätzliche Cross-Referenz Tabelle mit dem Spalten Kunden ID und Object ID. Für jedes Objekt das der Kunde besitzt wir dort dann ein Eintrag gemacht.
Wenn man noch weiter in der Normalisierung gehen würde, dann würde man sogar die Adresse heraus nehmen und eine Adress-Tabelle erstellen. Damit kann dann, nach anlegen einer Cross-Referenz-Tabelle der Kunde auch mehrer Adressen haben, oder aber man legt in der X-Tabelle noch die Spalte "Expired at" als DATETIME an und kann somit eine Adresse auf invalid setzen und eine neue erstellen wenn der Kunde umzieht. Somit kannst du immer anchverfolgen wann der Kunde wo gewohnt hat und wohin zu diesem Zeitpunkt die Rg etc gegangen ist.

Gruß

Claus


----------



## jhjh (7. Feb 2019)

Hallo,

Vielen Dank erstmal für die Mühe beim Leses des langen Threads und auch für die sehr schnelle Antwort!


> wenn Du eine Tabelle mit den möglichen Werten für die Zahlungsart hast, wozu hast du diese dann noch einmal in den Kunden drin


Nein ich habe keine zusätzliche Tabelle für die Zahlungsart. Das befindet sich alles in meiener Tabelle *Kunde
*
Ok und beim Zahlungsintervall und dem Zahlungszeitpunkt genau das selbe ?
Also quasi so:

*tbl Kunde
KnId*-> Nname -> ....ZahlungsartID -> Zahlungsintervall -> Zahlungszeitpunkt->Objekt
1 ->Müller->....->2->3->2->1
(Bezahlt alle 2 Monate am Monatsende und besitzt einen Kreis)

*tbl Zahlungsart*
ZahlungsartID -> Beschreibung
1-> Rechnungszahler
2-> Barzahler

*tbl Zahlungsintervall*
ZahlungsintervallID-> Beschreibung
1-> Wöchentlich
2-> Monatlich
3-> 2-Monatlich
4 ->3-Monatlich

*tbl Zahlungszeitpunkt*
ZahlungszeitpunktID-> Beschreibung
1->Monatsanfang
2-> Monatsende

*tbl Objekt
ObjektID -> Beschreibung*
1-> Kreis
2-> Polygon



> Das gleiche gilt für die Objekte. Du hast eine Tabeller mit Objekten (Kreis, Polygon was auch immer). Jedes Object hat eine unqiue ID. In der Kundentabelle steht dann nur die ID des Objektes.


Das Problem hierbei ist, dass ich bei *tbl Kunde* bzgl. des Kreises/Poygons 2 Informationen benötige. Zum einen um was für ein Objekt es sich hierbei handelt (Kreis oder Polygon) und zum anderen um welche "Art" Kreis/Polygon es sich handelt. Beim Kreis wird hinsichtlich der Größe unterschieden und beim Polygon hinsichtlich der Koordinaten der Eckpunkte (Wird im Json String gespeichert). Wie mache ich es hier am besten ?



> Wenn man noch weiter in der Normalisierung gehen würde, dann würde man sogar die Adresse heraus nehmen und eine Adress-Tabelle erstellen. Damit kann dann, nach anlegen einer Cross-Referenz-Tabelle der Kunde auch mehrer Adressen haben, oder aber man legt in der X-Tabelle noch die Spalte "Expired at" als DATETIME an und kann somit eine Adresse auf invalid setzen und eine neue erstellen wenn der Kunde umzieht. Somit kannst du immer anchverfolgen wann der Kunde wo gewohnt hat und wohin zu diesem Zeitpunkt die Rg etc gegangen ist.


Danke, aber ich glaube das passt schon so 
In der aktuellen Form wäre die Datenbank in der 3. Nf, liege ich da richtig ?


----------



## mrBrown (7. Feb 2019)

jhjh hat gesagt.:


> *KnId*-> Nname -> ....ZahlungsartID -> Zahlungsintervall -> Zahlungszeitpunkt->Objekt


Deine "->" haben keine Bedeutung, sondern sind nur Trennzeichen, oder? ("->" drückt meist eine Beziehung aus)




jhjh hat gesagt.:


> Nein ich habe keine zusätzliche Tabelle für die Zahlungsart. Das befindet sich alles in meiener Tabelle *Kunde*


Wenn ich @Thallius richtig verstanden habe, meint er mit Zahlungsart nicht Rechnung oder Bar, sondern das Tupel aus Art, Intervall, Zeitpunkt.

Könnte man modellieren als:
*tbl Kunde
KnId*, Nname, ..., ZahlungsID, ObjectID

tbl Zahlung
*KnId*, Zahlungsart, ZahlungsIntervall, Zahlungszeitpunkt

tbl Objekt
*KnId*, Art, "JsonString", ...


Man könnte auch noch weitergehen, und getrennte Tabellen für Bar und Rechnung erstellen.



jhjh hat gesagt.:


> *tbl Zahlungsintervall*
> ZahlungsintervallID-> Beschreibung
> 1-> Wöchentlich
> 2-> Monatlich
> ...



Zahlung und Objekt dann noch so aufzudröseln, kann man machen, muss man aber nicht. Macht manches einfacher, anderes aber schwieriger. Bei rein lokaler App mit lokaler Datenbank würde ich es tendenziell nicht machen. 



jhjh hat gesagt.:


> Das Problem hierbei ist, dass ich bei *tbl Kunde* bzgl. des Kreises/Poygons 2 Informationen benötige. Zum einen um was für ein Objekt es sich hierbei handelt (Kreis oder Polygon) und zum anderen um welche "Art" Kreis/Polygon es sich handelt. Beim Kreis wird hinsichtlich der Größe unterschieden und beim Polygon hinsichtlich der Koordinaten der Eckpunkte (Wird im Json String gespeichert). Wie mache ich es hier am besten ?


Das kommt dann in die Objekt-Tabelle.


So ganz generell: gibt deinen Spalten bessere Namen, Abkürzungen zu verwenden ist 'ne Unsitte 
"*Kn*oten*Id*" (KnID) und "*V*erified *Name*" (Vname) kann man doch auch ausschreiben


----------



## jhjh (7. Feb 2019)

> Deine "->" haben keine Bedeutung, sondern sind nur Trennzeichen, oder? ("->" drückt meist eine Beziehung aus)


Ja habe ich als Trennzeichen benuzt. Zukünftlich werde ich dafür KOmmata benutzen, ist wohl besser 


> Könnte man modellieren als:
> *tbl Kunde
> KnId*, Nname, ..., ZahlungsID, ObjectID
> 
> ...


Ah, ich hatte bei mir noch die KnId vergessen! Sieht wohl ganz gut aus denke ich 


> Zahlung und Objekt dann noch so aufzudröseln, kann man machen, muss man aber nicht. Macht manches einfacher, anderes aber schwieriger. Bei rein lokaler App mit lokaler Datenbank würde ich es tendenziell nicht machen.


Dann lasse ich es mal lieber! Theoretisch könnte ich ja die "Art" bei tbl Objekt ja auch noch weiter aufdröseln, aber das werde ich auch lassen 



> So ganz generell: gibt deinen Spalten bessere Namen, Abkürzungen zu verwenden ist 'ne Unsitte
> "*Kn*oten*Id*" (KnID) und "*V*erified *Name*" (Vname) kann man doch auch ausschreiben


Na ja, in der Uni hat man immer dafür Abkürzungen benutzt, die ich mir dann ein wenig angewöhnt habe. Aber wenn am Anfang einer Tablle *Kunde *KnID/KnNr steht, wird man glaube nicht auf KnotenId kommen 
Aber ok Vorname/Nachname könnte man villeicht ausschreiben. 
Wird in der Praxis bei den Datensätzen häufig geküzt ? zB könnte ich bei der Zahlungsart ja auch Rz/Bz für Rechnungszahler/Barzahler benutzen. Ich denke aber das macht wohl kein Sinn 
btw: Wieso gibt es eigentlich beim Eröffnen eines Threads eine Vorschau Funktion, aber bei einer Antwort nicht ?


----------



## mrBrown (7. Feb 2019)

jhjh hat gesagt.:


> Ah, ich hatte bei mir noch die KnId vergessen! Sieht wohl ganz gut aus denke ich


Oh, bei mir sind die ZahlungsID und ObjectID noch überflüssig, die können weg.



jhjh hat gesagt.:


> Wird in der Praxis bei den Datensätzen häufig geküzt ? zB könnte ich bei der Zahlungsart ja auch Rz/Bz für Rechnungszahler/Barzahler benutzen. Ich denke aber das macht wohl kein Sinn


Ich zumindest mache es nicht, und würde es auch keinem empfehlen. Man spart ein paar Zeichen für weniger Verständlichkeit, du in zwei Monaten und jeder andere, der das Programm bearbeiten muss, wird dir dankbar sein 



jhjh hat gesagt.:


> btw: Wieso gibt es eigentlich beim Eröffnen eines Threads eine Vorschau Funktion, aber bei einer Antwort nicht ?


Findet sich unter "Weitere Einstellungen..." unter dem Textfeld


----------



## Thallius (7. Feb 2019)

mrBrown hat gesagt.:


> Oh, bei mir sind die ZahlungsID und ObjectID noch überflüssig, die können weg.
> 
> 
> Ich zumindest mache es nicht, und würde es auch keinem empfehlen. Man spart ein paar Zeichen für weniger Verständlichkeit, du in zwei Monaten und jeder andere, der das Programm bearbeiten muss, wird dir dankbar sein
> ...



Jetzt aber nur mal so ganz am Rande gefragt: Dir ist schon klar, dass bei jedem query die Namen geparst werden müssen. Sprich je länger der Name umso langsamer deine Software. Oder lernt man heutzutage wirklich das was ich immer wieder höre, dass man einfach schnellere Hardware braucht wenn die Software zu langsam ist?


----------



## mrBrown (7. Feb 2019)

Thallius hat gesagt.:


> Jetzt aber nur mal so ganz am Rande gefragt: Dir ist schon klar, dass bei jedem query die Namen geparst werden müssen. Sprich je länger der Name umso langsamer deine Software.



Da hast du natürlich recht, "KdID" statt "KundenID" ist wirklich die relevanteste Performanceoptimierung, die man in jedem Fall als aller erstes vornehmen sollte.



Thallius hat gesagt.:


> Oder lernt man heutzutage wirklich das was ich immer wieder höre, dass man einfach schnellere Hardware braucht wenn die Software zu langsam ist?



Jap, genauso wie man "damals" offensichtlich lernte, Code nur möglichst schnell hinzurotzen, mit möglichst schlechter Les- und Wartbarkeit und Verzicht auf jegliche Designprinzipien.


----------



## Thallius (7. Feb 2019)

mrBrown hat gesagt.:


> Da hast du natürlich recht, "KdID" statt "KundenID" ist wirklich die relevanteste Performanceoptimierung, die man in jedem Fall als aller erstes vornehmen sollte.
> 
> 
> 
> Jap, genauso wie man "damals" offensichtlich lernte, Code nur möglichst schnell hinzurotzen, mit möglichst schlechter Les- und Wartbarkeit und Verzicht auf jegliche Designprinzipien.



Stimmt und trotzdem hat es funktioniert...


----------



## jhjh (7. Feb 2019)

Was mich jetzt noch interessieren würde ist, in welcher Normalform sich die Datenbank in diesem Zustand befindet.  Meiner Meinung nach befindet sich die DB SO in der 3. Normalform,weil
-> Nur atomare Werte
->Jedes Schlüsselattribut ist vom gesamten Primätschlüssel voll funktional abhängig
-> Kein Nichtschlüsselattribut ist von einem anderen Nichtschlüsselattribut funktional abhängig!


----------



## mrBrown (8. Feb 2019)

jhjh hat gesagt.:


> Was mich jetzt noch interessieren würde ist, in welcher Normalform sich die Datenbank in diesem Zustand befindet.  Meiner Meinung nach befindet sich die DB SO in der 3. Normalform,weil
> -> Nur atomare Werte
> ->Jedes Schlüsselattribut ist vom gesamten Primätschlüssel voll funktional abhängig
> -> Kein Nichtschlüsselattribut ist von einem anderen Nichtschlüsselattribut funktional abhängig!



Der Json-String, in dem du Kreise/Polygone speicherst, ist abhängig von der Betrachtungsweise atomar oder eben nicht


----------



## jhjh (8. Feb 2019)

mrBrown hat gesagt.:


> Der Json-String, in dem du Kreise/Polygone speicherst, ist abhängig von der Betrachtungsweise atomar oder eben nicht


Sollte wohl in meinem Fall atomar sein, weil wenn ich den Polygon eines Kunden ändern möchte, dann wird der komplette Polygon geändert und nicht nur Teilinformationen. Somit atomar ? Oder welche Frage müsste ich mir selber stellen um das zu beantworten ?


----------



## mihe7 (8. Feb 2019)

jhjh hat gesagt.:


> Wird in der Praxis bei den Datensätzen häufig geküzt ?


Ich würde sagen, dass das auch heute noch weit verbreitet sein dürfte und bis zu einem gewissen Grad ist das m. E. auch völlig in Ordnung. 

Oracle hatte lange Zeit ein Limit von 30 Byte für die meisten Bezeichner. Warum man damit nicht auskommt, ist mir schleierhaft. Vermutlich sind das die gleichen Leute, deren Dateinamen länger als die jeweiligen Inhalte sind.

Inwiefern sich lange Bezeichner im "regulären Betrieb" auf die Performance auswirken, wie @Thallius anmerkte, kann ich nicht beurteilen. Es ist aber nicht allzu schwer, Fälle zu finden, in denen sich lange Bezeichner negativ auf die Performance auswirken müssen: große Skripte, die durch lange Bezeichner noch größer werden.

Das Thema der Geschwindigkeit führt zur Beantwortung Deiner Frage der Vorteile eines numerischen Datentyps gegenüber des alphanumerischen VARCHAR. 

Sobald Du Deine Daten verknüpfen musst, hat der numerische Datentyp die Nase klar vorne. Das geht je nach DBMS und die Umstände so weit, dass sich Anwender über die Geschwindigkeit des System beklagen.


----------



## jhjh (25. Mai 2019)

So ich muss das nochmal rauskramen, das lässt mir immoment keine Ruhe.
mrBrown hat mir bereits ein Vorschlag gemacht, wie ich meine Datenbank strukturieren könnte


mrBrown hat gesagt.:


> Könnte man modellieren als:
> *tbl Kunde
> KnId*, Nname, ...
> 
> ...


Mein Problem ist jetzt, dass nicht erkenne worin hier der Vorteil besteht, anstatt dass ich alles in eine Tabelle speichere.

tlb Kunde
*KnId*, Nname, Vname, Zahlungsart, Zahlungintervall, Art, JsonString......


Klar es ist irgendwie übersichtlicher, aber dies kann ja eigentlich kein Grund sein eine etwas größere Tabelle zu splitten ?


----------



## mihe7 (25. Mai 2019)

Es geht wie immer um Kompromisse, d. h. um Konsistenz, Mehrbenutzerbetrieb und Geschwindigkeit. 

Wenn Du Zahlungen hast, dann werden diese von (bzw. für) einen Kunden durchgeführt. Wenn ich mir eine Zahlung  ansehe, hilft es mir nicht, wenn ich weiß, dass der Kunde zum Zahlungszeitpunkt unter der Telefonnummer 0144242818 erreichbar gewesen ist und er in der Augustenstr. 3, 12345 Hinterdupfing gewohnt hat. Wenn ich ihn erreichen möchte, brauche ich aktuelle Daten. 

Umgekehrt kann es sinnvoll, manchmal notwendig sein, Daten redundant zu speichern. Aus Gründen der Geschwindigkeit für Auswertungen wird von der Normalform abgewichen. Statt sich zum Zeitpunkt der Auswertung die Daten erst zusammenzusuchen, werden diese gleich passend in die DB geschrieben. Bei z. B. Rechnungen bleibt einem gar nichts anderes übrig, als sämtliche Rechnungsdaten ggf. redundant zu speichern. Es darf nicht passieren, dass sich eine Rechnung ändert, weil der Kunde umgezogen ist oder der Preis eines Artikels angepasst wurde.


----------



## jhjh (25. Mai 2019)

mihe7 hat gesagt.:


> Wenn ich mir eine Zahlung  ansehe, hilft es mir nicht, wenn ich weiß, dass der Kunde zum Zahlungszeitpunkt unter der Telefonnummer 0144242818 erreichbar gewesen ist und er in der Augustenstr. 3, 12345 Hinterdupfing gewohnt hat. *Wenn ich ihn erreichen möchte, brauche ich aktuelle Daten.*


Also erstmal zum Verständnis: Die o.g. Tabellen erfassen nicht die Zahlungseingängen von Kunden, diese werden in einer weiteren Tabelle tblZahlungseingang gespeichert. Hier erkenne ich auch wirklich den Sinn eine extra Tabelle anzulegen, da ich ansonsten viele redudante Daten hätte. tblZahlung (heißt jetzt tblZahlmodel) wird nur festgehalten *wie *der Kunde bezahlt. Es sind also alles mehr oder wenig statische Daten. Mit der Geschwindigkeite meinst du, dass es bei der Abfrage länger dauern würde wenn Informationen aus großen Tabelle abgefragt werden anstatt aus kleinen Tabellen !? Ja ok! Allerdings werden während der Laufzeit eh keine Daten explizit angesfragt. Stattdessen werden die Kunden zu begin alle ausgelsen und in Listen gespeichert, mit denen ich dann arbeite.


----------



## mihe7 (25. Mai 2019)

jhjh hat gesagt.:


> Also erstmal zum Verständnis: Die o.g. Tabellen erfassen nicht die Zahlungseingängen von Kunden, diese werden in einer weiteren Tabelle tblZahlungseingang gespeichert.


Wie soll ich dann 


jhjh hat gesagt.:


> worin hier der Vorteil besteht, anstatt dass ich alles in eine Tabelle speichere.
> 
> tlb Kunde
> *KnId*, Nname, Vname, Zahlungsart, Zahlungintervall, Art, JsonString......


verstehen?



jhjh hat gesagt.:


> Mit der Geschwindigkeite meinst du, dass es bei der Abfrage länger dauern würde wenn Informationen aus großen Tabelle abgefragt werden anstatt aus kleinen Tabellen !?


Jein. In der Regel geht es dabei um aggregierte Daten. Nimm beispielsweise den Kontostand. Der ergibt sich automatisch aus Zu- und Abgängen. Wenn Du jetzt aber eine Tabelle mit Konten inkl. des Kontostands anzeigen willst, dauert die Abfrage einfach zu lange, weil Du sämtliche Buchungen nach Konto gruppiert summieren müsstest.


----------



## jhjh (25. Mai 2019)

mihe7 hat gesagt.:


> Wie soll ich dann
> ...
> verstehen?


Siehe mein ersten Beitrag. Es geht hier darum zu benennen wie und wann ein Kunde bezahlen muss. Jeder Kunde entscheidet sich vorab ob er ein Rechnungszahler oder ein Barzahler sein möchte. Bei Rechnungszahlern wird immer am Monatsende bezahlt. Bei Barzahlern wird nach dem Zahlungsintervall (Wöchentlich, Monatlich etc) und nach dem Zeitpunkt der Bezahlung (Monatsanfang/Monatsende) unterschieden. 

Oder wolltest du auf was anderen hinaus ?


----------



## Meniskusschaden (25. Mai 2019)

mihe7 hat gesagt.:


> Bei z. B. Rechnungen bleibt einem gar nichts anderes übrig, als sämtliche Rechnungsdaten ggf. redundant zu speichern. Es darf nicht passieren, dass sich eine Rechnung ändert, weil der Kunde umgezogen ist oder der Preis eines Artikels angepasst wurde.


Aber durch weitere Normalisierung ergeben sich Alternativen, z.B. indem man die Adressdaten in eine eigene Tabelle auslagert. Beim Umzug erstellt man eine neue Adresse und verknüpft deren ID mit dem Kundenstamm. Alte Rechnungen behalten ihre Adress-ID, neue Rechnungen bekommen die neue. Dann sind wie üblich wieder nur die Schlüssel redundant.


----------



## jhjh (25. Mai 2019)

> Alte Rechnungen behalten ihre Adress-ID, neue Rechnungen bekommen die neue.


In dem Anwendungskontext wird keine Adress-ID benötigt. Zu jedem Zahlungseingang wird nur das Datum und die KnId gespeichert.



tblZahlungseingänge
*KnId*,Datum_Zahlungseingang

Jede einzelne Rechnung "besitzt" dann halt die Adresse die in tblKunde jeweils gespeichert ist.


----------



## Meniskusschaden (25. Mai 2019)

jhjh hat gesagt.:


> In dem Anwendungskontext wird keine Adress-ID benötigt.


Das habe ich auch nicht behauptet. Ich wollte nur das "bleibt einem gar nichts anderes übrig" aus Posting #15 von @mihe7 relativieren. Er hat aber ein reales Problem angesprochen und das:


jhjh hat gesagt.:


> tblZahlungseingänge
> *KnId*,Datum_Zahlungseingang
> 
> Jede einzelne Rechnung "besitzt" dann halt die Adresse die in tblKunde jeweils gespeichert ist.


ist keine Lösung dafür.


----------



## Thallius (25. Mai 2019)

Ganz einfach frage. Was ist wenn der Kunde seine Zahlungsmethoden ändern will? Dann kannst du im Moment nicht wissen ab wann er die neue Zahlungsverzug benutzen möchte, BTW wie lange die alte gilt. Deswegen auch hier eine eigene Tabelle mit der Zahlungsart in einer 1:n Beziehung und ein paar mehr Angaben als du sie gerade hast. U.a. Eben. Ein „startDate“ oder sowas.

Gruß

Claus


----------



## jhjh (25. Mai 2019)

Thallius hat gesagt.:


> Ganz einfach frage. Was ist wenn der Kunde seine Zahlungsmethoden ändern will? Dann kannst du im Moment nicht wissen ab wann er die neue Zahlungsverzug benutzen möchte, BTW wie lange die alte gilt. Deswegen auch hier eine eigene Tabelle mit der Zahlungsart in einer 1:n Beziehung und ein paar mehr Angaben als du sie gerade hast. U.a. Eben. Ein „startDate“ oder sowas.
> 
> Gruß
> 
> Claus


Das was ich hier bisher an Tabellen genannt habe ist nur ein Teil der Datenbank. Über eine Tabelle _tblZugang_ wird das Datum erfasst wann ein neuer Kunde dazugekommen ist. Ändert der Kunde die Zahlungsmethode wird das Zugangsdatum aktuallisiert. Er wird quasi als Neuer Kunde behandelt.  Der Anwender wird darüber in Kenntniss gesetzt. In dem Anwendungskontext ist das erlaubt.


----------



## mrBrown (25. Mai 2019)

Und in drei Monaten gibts dann die Anforderung, dass es so doch nicht geht, und man beißt sich ins Bein, dass man es nicht von Anfang an „richtig“ gemacht hat ;P


----------



## mihe7 (25. Mai 2019)

Meniskusschaden hat gesagt.:


> Aber durch weitere Normalisierung ergeben sich Alternativen, z.B. indem man die Adressdaten in eine eigene Tabelle auslagert. Beim Umzug erstellt man eine neue Adresse und verknüpft deren ID mit dem Kundenstamm. Alte Rechnungen behalten ihre Adress-ID, neue Rechnungen bekommen die neue. Dann sind wie üblich wieder nur die Schlüssel redundant.


Im Prinzip hast Du völlig Recht. Wir sind irgendwann zu dem Schluss gekommen, dass wir Dokumente auch als solche behandeln: wir speichern die Inhalte komplett inkl. aller Daten separat und damit in der Regel redundant. Das ist jetzt zig Jahre her, vielleicht sollte ich mal darüber nachdenken, ob ich das heute wieder so machen sollte


----------



## Meniskusschaden (25. Mai 2019)

jhjh hat gesagt.:


> Das was ich hier bisher an Tabellen genannt habe ist nur ein Teil der Datenbank. Über eine Tabelle _tblZugang_ wird das Datum erfasst wann ein neuer Kunde dazugekommen ist. Ändert der Kunde die Zahlungsmethode wird das Zugangsdatum aktuallisiert. Er wird quasi als Neuer Kunde behandelt. Der Anwender wird darüber in Kenntniss gesetzt. In dem Anwendungskontext ist das erlaubt.


Hm, ging es bei der Frage


jhjh hat gesagt.:


> Klar es ist irgendwie übersichtlicher, aber dies kann ja eigentlich kein Grund sein eine etwas größere Tabelle zu splitten ?


nun um die Nachteile denormalisierter Daten oder nur darum, ob du mit den Nachteilen leben kannst? Man kann natürlich immer technische oder organisatorische Workarounds finden und manchmal kann das auch der pragmatischere Weg sein. Das ändert aber doch nichts an den prinzipiellen Nachteilen. Normalerweise entstehen auch nach der Einführung noch viele neue Anforderungen. Mit vernünftig normalisierten Daten ist man da in der Regel flexibler aufgestellt, als wenn man aufpassen muß, dass alte Workarounds nicht platzen.


----------



## jhjh (25. Mai 2019)

Meniskusschaden hat gesagt.:


> Hm, ging es bei der Frage
> nun um die Nachteile denormalisierter Daten oder nur darum, ob du mit den Nachteilen leben kannst?


Mir ging es um die Nachteile von "großen" Tabellen gegenüber das Splitten einer solchen Tabelle (
so wie es mir in #4 vorgeschlagen wurde). Ich hatte gedacht man splitten immer aufgrund von Normalisierung. Da sich in dem Beispiel aber der Normalisierungsgrad nicht geändert, war ich ein wenig verwundert.

Geändert an der Struktur wird jetzt eh nichts mehr  Auch wenn es hinsichtlich der Wartung nicht optimal ist ^^


----------



## mihe7 (26. Mai 2019)

jhjh hat gesagt.:


> Mir ging es um die Nachteile von "großen" Tabellen gegenüber das Splitten einer solchen Tabelle


Das liegt auf der Hand: werden thematisch nicht zusammengehörige Attribute in einer Tabelle zusammengefasst, führt man für diese weitgehend unabhängigen Attribute eine Abhängigkeit ein. Sie können also weder unabhängig voneinander verändert werden, noch kann der Zugriff unabhängig geregelt werden.



jhjh hat gesagt.:


> Da sich in dem Beispiel aber der Normalisierungsgrad nicht geändert, war ich ein wenig verwundert.


Das Beispiel ist in 3. NF, Deine Tabelle aus #1 dagegen nicht.


----------



## jhjh (26. Mai 2019)

mihe7 hat gesagt.:


> Das Beispiel ist in 3. NF, Deine Tabelle aus #1 dagegen nicht.


Hm, da bin ich anderer Meinung. Dass die Tabelle aus #1 mindestens in der 2. Nf ist, sind wir uns einig, oder ? Die Bedingung für die 3. Nf ist die, dass unter den Nicht-Schlüssel-Attributen keine Abhängigkeiten herrschen. Sprich: Ich kann nicht von einem Attribut auf das andere schließen. Das sollte meiner Meinung eigentlich der Fall sein. Ich kann lediglich anhand von einzelnen Attributen den Werteberech anderer Attribute bestimmen. Beispielsweise bei der Bezahlung: Falls ein Kunde ein Barzahler ist, dann kann ich sagen, dass der Zahlungsintervall !null ist. Ich kann aber nicht explizit sagen, dass es ein Wöchentlicher/Monatlicher etc. bezahler ist.


----------



## mihe7 (26. Mai 2019)

Deine Tabelle aus #1 ist nicht in 3. NF, weil aus Kreis != null => Polygon == null und umgekehrt.


----------



## jhjh (26. Mai 2019)

mihe7 hat gesagt.:


> Deine Tabelle aus #1 ist nicht in 3. NF, weil aus Kreis != null => Polygon == null und umgekehrt.


Ja schon, aber


> Wenn der Kunde einen Kreis besitzt, dann kann dieser unterschiedliche Werte einnehmen (2 , 3 .... 200 ....)


Heißt wenn Polygon *null *ist, kann ich nur sagen, dass Kreis *!= null* ist, ich kann aber *nicht eindeutig* den Wert bestimmen.


----------



## mrBrown (26. Mai 2019)

jhjh hat gesagt.:


> Heißt wenn Polygon *null *ist, kann ich nur sagen, dass Kreis *!= null* ist, ich kann aber *nicht eindeutig* den Wert bestimmen.


Wenn Polygon != null, kann man Kreis eindeutig bestimmen


----------



## jhjh (26. Mai 2019)

mrBrown hat gesagt.:


> Wenn Polygon != null, kann man Kreis eindeutig bestimmen


Ja schon. Aber ich hätte jetzt gedacht, dass eine Abhängigkeit dann  besteht, wenn egal welchen Wert ein Attribut einnimmt, ich immer ein anderes Attribut bestimmen kann. Wenn Polygon != null -> Kreis == null ; Wenn Polygon == null -> *Kreis == ?*


----------



## jhjh (26. Mai 2019)

Bedeutet dein Like dass ich recht habe ?  Somit wäre das aus #1 in der 3. Nf


----------



## mihe7 (26. Mai 2019)

@jhjh Die Überlegung ist gut (daher der Like), denn formal betrachtet ist das keine funktionale Abhängigkeit, weil für einen Wert aus Polygon der Kreis nicht eindeutig bestimmbar ist.

Damit habe ich allerdings ein Problem, denn eine erhebliche Abhängigkeit zwischen Polygon und Kreis ist nicht von der Hand zu weisen, schließlich existiert eine funktionale Abhängigkeit für alle Polygon != null (bzw. Keis != null).

Wie sich der Widerspruch auflösen lässt: Du hast ein zusätzliches Attribut, das in der Tabelle fehlt, weil Du es aus den Werten implizit ableitest, nämlich den Typ. Wenn Du also gedanklich den Typ mit in die Tabelle aufnimmst, dann folgt aus Polygon != null && Kreis == null => Typ = 'Polygon' und aus Polygon == null && Kreis != null => Typ = 'Kreis'.


----------



## jhjh (26. Mai 2019)

Ich hatte mal ein Attribut 'Typ' drinn. Hatte ich aber entfernt, weil dieser unnötig ist, da wie du schon sagtest, ich dies aus den Werten implizit ableiten kann.  Dass eine gewisse Abhängigkeit zwischen Kreis und Polygon (und auch zwischen anderen Attributen) herrscht ist mir klar, nur halt irgendwie keine"vollständige Abhängigkeit"


----------



## mihe7 (26. Mai 2019)

jhjh hat gesagt.:


> Ich hatte mal ein Attribut 'Typ' drinn.


Aha! Hättest Du an der Stelle normalisiert, wärst Du aufgrund der oben gezeigten transitiven Abhängigkeit auch zu der Objekt-Tabelle gekommen, die Dir @mrBrown in Kommentar #4 gezeigt hat.


----------



## jhjh (26. Mai 2019)

Ne ich hatte die ja erst drinn als er mir das vorgeschlagen hatte! Davor hatte ich das noch nicht ^^


----------



## mihe7 (26. Mai 2019)

Dort wäre das Attribut Typ (bzw. Art) aber nicht überflüssig. Auf jeden Fall ist es interessant zu sehen, dass man durch das Entfernen von Attributen formal eine NF herstellen kann, die sich bei Abbildung des konzeptionellen Modells nicht ergeben würde.


----------



## jhjh (26. Mai 2019)

mihe7 hat gesagt.:


> Dort wäre das Attribut Typ (bzw. Art) aber nicht überflüssig. Auf jeden Fall ist es interessant zu sehen, dass man durch das Entfernen von Attributen formal eine NF herstellen kann, die sich bei Abbildung des konzeptionellen Modells nicht ergeben würde.


Ok, Danke!


----------

