SQL Welche Normalisierungsform?

selberbauer

Captain
Registriert
Juni 2009
Beiträge
3.604
Hallo,

ich entwickle gerade das Datenbank Layout für eine Webanwendung in Symfony2.

Nun wollte ich mal wissen, was erfahrungsgemäß "besser" ist.
Die 2. NF oder die 5. NF?

Angenommen ich verwalte Personendaten, welche NF empfiehlt sich da (2. oder 5.)?

2. NF:
id | last_name | first_name | birth | street | nr | email

5. NF
main:
id | last_name | first_name | birth_id | addresse_id | mail_id
1 | müller | horst | 1 | 1 | 1
birth:
id | date
1 | 2000-01-04
addresse:
id | street | nr | zip_code
....

Gruß,
selberbauer
 
für z.B. birth würde ich die 2 NF nehmen, jeder hat nur einen.
Wenn über birth gesucht werden soll, besser einen Index anlegen.

Bei address sieht es anders aus, es soll ja Menschen mit mehreren Adressen geben. Kommt auf die Anwendung an.

Die 5 NF verursacht höhere "Kosten", sofern man nicht über einzelne Spalten sucht, die nicht sortiert sind.
 
Um hier mal aufzuspringen: Hat jemand eine gute Einleitung zu den verschiedenen Formen? Der Wiki-Artikel ist zwar schon recht informativ, aber suche etwas grundlegenderes, um zB den Kosten-Nutzen-Faktor abwägen zu können und überhaupt.

Hab' mich nämlich noch nie wirklich damit auseinandergesetzt, aber es klingt auf jeden Fall spannend. Danke!
 
@ripuli-6
Kosten-Nutzen-Faktor ist von den konkreten Daten abhängig, pauschal ist keine korrekte Aussage möglich.

Zusammengefasst lässt sich sagen, dass
  • der Nutzen einer höhere Normalform,
    die Vermeidung von Redundanzen und somit weniger Speicherverbrauch ist.
  • Als Kostenfaktor müssen die Daten beim Auswerten erst wieder zusammengebaut werden,
    dies benötigt CPU/HDD-Leistung und mehr Zeit.

Es kann also durchaus beabsichtigt sein, eine niedrige Normalform zu erstellen.
 
Zuletzt bearbeitet:
Sorry, kenne keine Literatur über das Thema. Gibt es bestimmt, mal googln.

Ich kenne es nur aus praktischer Erfahrung. "Bessere" DB-Systeme zeigen bei Bedarf die Kosten einer Abfrage an. Mal ausprobieren, es ist enorm, was z.B. ein richtig gesetzter Index die Laufzeit verbessern kann.
 
Danke. Wenn Du erlaubst mache ich mal kurz ein Beispiel, um zu gucken, ob ich das auch wirklich verstanden habe.

Nehmen wir mal ein einfaches Login Script an, bei dem jeder Benutzer mehrere Email Adressen hat. In dem Falle kann ich die Tabelle wie folgt aufbauen:
1. TB
ID | User | Password | Status
1 | ripuli | 1234 | 'user'
2 |yxcv |5678 | 'admin'

2. TB
ID |userID |email
1 | 1 | sag@ich.net
2 | 1 | müh@lambda.net
3 | 2 | egal@auch.mir

Hinweis: Status ist Enum, zB 'admin','user','inactive'

In dem Falle wäre es sinnvoll die Daten so aufzubereiten, weil ich schließlich nicht den ganzen anderen Krams (password, status etc.) zweimal zu stehen haben will. Denn hätte ich eine Tabelle mit zwei Zeilen für User mit zwei Email-Adressen und ich will das Passwort ändern, muss ich beide Zeilen ändern, was recht aufwändig und fehleranfällig wäre.

Ist es denn eigentlich sinnvoll in den normalisierten Tabellen auch einen Index anzulegen? Zunächst erscheint es ja, als ein erhöhter Verbrauch von Speicherplatz, aber ich könnte ja später Anwendungen entwickeln, die andere Assoziationen und Daten haben und dann bräuchte ich eventuell den Index?
 
Pauschal kann man nicht sagen, ob ein Index angelegt werden soll.

Wenn man aber weiß, dass es häufige Abfragen über eine bestimmte Spalte gibt, lohnt sich ein Index, trotz erhöhtem Platzbedarf.

Wird in dem Beispiel nach einer email-Adresse gesucht, muss ohne Index die Tabelle von anfang an gescannt werden. Mit Index springe schnell zur gesuchten email.

D.h., habe ich eine neue Anwendung für die DB, muss ich mir die wichtigsten Selects anschauen und geg. einen neuen Index anlegen

edit: im Beispiel kann man allerdings auch die 2.TB ändern:

ID - email - Userid

Dann wird eh nach email sortiert - Habe aber dann höhere Kosten, wenn ich nach User suche.
 
Zuletzt bearbeitet:
Warum möchtest du explizit die 2. oder 5. haben? Die 3. mit zusätzlicher BC-NF wäre ein guter Kompromiss
 
Warum möchtest du explizit die 2. oder 5. haben? Die 3. mit zusätzlicher BC-NF wäre ein guter Kompromiss

Kann man den sagen, dass es auf das Anwendungsgebiet ankommt (ab 2. NF) oder das man sich auf eine einigen sollte?
 
Also mir wurde noch beigebracht das man eigentlich immer bis zur 3. NF gehen sollte. Alles darüber ist abhängig vom Anwendungsfall.
 
1. NF:
Jedes Feld ist atomar. Ein Feld enthält immer nur ein Attribut.
Falsch: | id* | addresse |
Richtig: |id* | strasse | nr |

2. NF:
Jede Spalte hat eine einzigartigen Primärschlüssel(*)
Falsch: | nachname* | vorname*
Richtig: | id* | nachname | vorname

3. NF
Ein Nichtschlüsselattribut darf nicht von einem Schlüsselkanidaten transitiv abhängen.
Solche Abhängigkeiten sind in eine extra Tabelle zueinander zu setzen.
Falsch: | kunden_id* | vermoegen | bank | bank-nr.
Richtig: | bank | bank_nr. |

Und was macht jetzt die BCNF?
 
selberbauer schrieb:
2. NF:
Jede Spalte hat eine einzigartigen Primärschlüssel(*)
Falsch: | nachname* | vorname*
Richtig: | id* | nachname | vorname

Vermutlich ein Versehen - denn hier ist sicher "Jede Zeile" gemeint.

edit: Um nochmal auf die eigentliche Frage einzugehen. Ich kann hier nur Mike Lowrey zustimmen. Die 3. NF sollte in der Regel zum Einsatz kommen. Alles andere ist vom Anwendungsfall und den damit verbundenen Faktoren abhängig. Wie z.b. Art der Daten, Datenmenge, Datensicherheit, Datenverwendung/Zugriffshäufigkeit.

Und nicht zu vergessen: Um so komplexer dein Datenmodell ist, desto mehr musst du auf Datenintigrität achten. Sprich, wenn du Datensätze modifizierst, muss darauf geachtet werden, dass auch alle Abhängigen Tabellen und Attribute aktualisiert werden. Das lässt sich über Constrainst bewerkstellen, führt aber mit steigender NF auch zu erhöhtem Aufwand.

Bei der Trennung deiner Daten ist es ein guter Ansatz die Art zu berücksichtigen. Im oben genannten Beispiel sind aus meiner Sicht nur Personen-"Stammdaten" aufgeführt. Es ergibt wenig Sinn diese Daten in unterschiedlichen Tabellen vorzuhalten. Geburtstag und Adresse gehören zu den Stammdaten und sollten somit auch dort abgelegt werden. Zumal mir gerade die Redundanz der Adresse sehr unwahrscheinlich erscheint.
 
Zuletzt bearbeitet:
ripuli-6 schrieb:
In dem Falle wäre es sinnvoll die Daten so aufzubereiten, weil ich schließlich nicht den ganzen anderen Krams (password, status etc.) zweimal zu stehen haben will. Denn hätte ich eine Tabelle mit zwei Zeilen für User mit zwei Email-Adressen und ich will das Passwort ändern, muss ich beide Zeilen ändern, was recht aufwändig und fehleranfällig wäre.
solange du noch keine Ahnung von Datenbankperformance hast, würde ich das aufsplitten von Informationen sein lassen, denn dein Beispiel hätte aus Sicht für z.B. MySQL keinerlei Performance-Relevanz gehabt.

ripuli-6 schrieb:
Ist es denn eigentlich sinnvoll in den normalisierten Tabellen auch einen Index anzulegen? Zunächst erscheint es ja, als ein erhöhter Verbrauch von Speicherplatz, aber ich könnte ja später Anwendungen entwickeln, die andere Assoziationen und Daten haben und dann bräuchte ich eventuell den Index?
Einen Index benötigst du um Bedingungen zu erfüllen, eindeutige Werte: Primärschlüsse, Unique Schlüssel oder um die Suche nach Datensätzen mit der gewünschten Spalte zu beschleunigen: Ohne Index wird vom Anfang alles gesucht.






selberbauer, deine Definitionen sind falsch, also nur die 1. stimmt ;)

2. Normalform
Jedes Attribut muss von allen Teilen des Primärschlüssels abhängen (besteht der Primärschlüssel nur aus einer Spalte, ist die Regel autom. erfüllt):

Code:
 #cdid | #tracknr | albumtitel | interpret | titel
Falsch da: Albumtitel und Interpret hängen nur von der CD-Id und nicht der Tracknr ab, nur der Titel hängt von beiden ab

Dein Definition ist falsch, weil es sehrwohl Datenbanken mit geteilten Primärschlüsseln geben darf, schau dir mal an, was eine "n:m-Beziehung" ist ;)

3. Normalform
Jedes Attribut muss vom Primärschlüssel abhängen
Code:
#cdid | albumtitel | interpret | jahr der gründung
Falsch da: das Jahr der Gründung hängt nicht von der CD-Id ab, sondern von dem Interpreten.

Edit: deine 3. NF stimmt doch, wenn du Bank zu einem Primärschlüssel machst, was bei dir aktuell nicht der Fall ist.


Die BCNF und alles darüber kannst du getrost ignorieren, die BCNF tritt z.B. nur in sehr speziellen Fällen ein und ist relativ komplex und mit der 3. NF ist deine Datenbank schon gut normalisiert. Bisher habe ich die BCNF und 4. NF auch nur in Uni-Unterlagen und deren Übungen gesehen, die sind schon etwas theoretischer Natur.



Erfüllen solltest du immer die 1. Normalform*, ob du die 2. und 3. Normalform ebenfalls erfüllst, ist dir überlassen, es entstehen auch jedenfall deutlich besser strukturierte Datenbanken die anpassungsfreundlicher sind, jede weitere Stufe der Normalform geht aber auf Kosten der Datenbankperformance, sofern du dich in Datenbankperformance aber noch nicht sehr gut auskennst, ignorierst du diese Ausnahme und erfüllst immer die Anforderungen bis zur 3. NF.


* auch die 1. NF darf aus Gründen der Datenbankperformance gebrochen werden, aber dann muss man wirklich schon sehr sehr sehr gute Gründe haben, das man solch eine elementare Normalform brechen darf. Anmerkung: atomare Daten pro Spalte darf nie gebrochen werden, einzig die verknüpfte Anforderung, dass es keine Aufzählungsspalten wie telefonnummer1, telefonnummer2, ... geben darf.
 
Zuletzt bearbeitet:
@ice-breaker

Danke für deine Ausführung.
Habe das mal gleich auf den Merkzettel geschrieben ;)

3. NF als Standard ansonsten je nach Anwendung.

Besonders interessieren mich da die Stamminformationen, die du bereits erwähnt hast.
1. Da heutzutage fast jeder mindestens zwei Telefonnummern hat, würde es sich nicht anbieten diese in einer extra Tabelle zu speichern (1:n)
| id | nachname | vorname | ... | telefon_id
telefon_id | telefon_nr
2. Wie sieht es mit der Addresse aus?
Bei drei Informationen ( Straße, Nr, PLZ) würde sich eine extra Tabelle evtl. schon aus Performance-, Übersichtsgründen anbieten.
Da das ORM (z.B. Doctrine) nur die Tabellen lädt, welche benötigt werden.
3. Telefonnummern
Wie sollte man das mit den Vorwahlen machen?
Manche schreiben ja 030 837498 andere wieder 030378456, dann manche
030 838 9383. Sollte man sowas versuchen im Entity per Constrain (also per Validierung) zu Kontrollieren oder dies in Spalten aufteilen?

Gruß
 
selberbauer schrieb:
Besonders interessieren mich da die Stamminformationen, die du bereits erwähnt hast.
1. Da heutzutage fast jeder mindestens zwei Telefonnummern hat, würde es sich nicht anbieten diese in einer extra Tabelle zu speichern (1:n)
| id | nachname | vorname | ... | telefon_id
telefon_id | telefon_nr
und wie speicherst du nun 2 Telefonnummern? Schau dir mal n.m-Beziehungen und 1:n-Beziehungen an, du hast die Info falsch gespeichert. Du hättest: "user_id | telefon_nr" machen müssen.


selberbauer schrieb:
2. Wie sieht es mit der Addresse aus?
Bei drei Informationen ( Straße, Nr, PLZ) würde sich eine extra Tabelle evtl. schon aus Performance-, Übersichtsgründen anbieten.
Bitte mache keine Aussagen über Peformance sofern du kein tiefes Verständnis hast, wie eine Datenbank intern arbeitet und wie deine gewählte Datenbank arbeitet, es geht nach hinten los, glaube mir, ich mache sehr viel MySQL-Optimierung ;)
Aus Performance-Sicht würde es null komma keinen Unterschied machen, du kannst es aber natürlich aus Übersichtlichkeitsgründen machen.


selberbauer schrieb:
Da das ORM (z.B. Doctrine) nur die Tabellen lädt, welche benötigt werden.
3. Telefonnummern
Wie sollte man das mit den Vorwahlen machen?
Manche schreiben ja 030 837498 andere wieder 030378456, dann manche
030 838 9383. Sollte man sowas versuchen im Entity per Constrain (also per Validierung) zu Kontrollieren oder dies in Spalten aufteilen?
du musst deine Daten immer in eine gemeinsame Struktur bringen, bevor du sie abspeicherst, und das schon vor Doctrine.
Also entweder du sorgst selbst dafür, dass alle Telefonnummern eine gleiche Struktur bekommen oder du speicherst die Info eben ohne jegliche Struktur, aber dann kannst du mit dieser auch nicht mehr weiterarbeiten, du wirst sie nur Speichern, Lesen und Ausgeben können aber mehr nicht.
 
Zurück
Oben