Warum ist das relationale Modell für eine Datenbank wichtig?

61

Ich nähere mich einem Projekt, bei dem ich mit meinem Chef eine Datenbank implementieren muss. Wir sind ein sehr kleines Start-up, daher ist das Arbeitsumfeld zutiefst persönlich.

Er hatte mir zuvor eine der Unternehmensdatenbanken gegeben, die völlig gegen das verstieß, was mir in der Schule für RDBMS beigebracht wurde (und worüber ich gelesen habe). Beispielsweise gibt es hier ganze Datenbanken, die aus einer Tabelle bestehen (pro unabhängiger Datenbank). Eine dieser Tabellen ist mehr als 20 Spalten lang. Für den Kontext sind hier einige der Spaltennamen aus einer Tabelle aufgeführt:

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName

Der Punkt ist, dass er, wo er einzelne Tabellen haben sollte, die die Entitätsdaten enthalten (Name, Größe, Kaufdatum usw.), alles in eine große Tabelle pro Datenbank schiebt.

Ich möchte dieses Design verbessern, bin mir jedoch nicht sicher, warum ein ordnungsgemäß normalisiertes und segmentiertes Datenmodell dieses Produkt tatsächlich verbessern würde. Ich kenne mich zwar mit Datenbankdesign aus dem College aus und verstehe, wie das geht, bin mir aber nicht sicher, warum dies die Datenbanken tatsächlich verbessert.

Warum verbessert ein gutes relationales Schema eine Datenbank?

8 Protonen
quelle
33
Ein Wort: Normalisierung.
Robert Harvey
9
Enger Wähler - rechtfertigen Sie sich! :-)
Robbie Dee
6
Es ist üblich, dass neue Mitarbeiter etablierte Verfahren kritisieren, ohne die Gründe dafür zu kennen, auch wenn diese technisch nicht stichhaltig sind. Finden Sie zuerst heraus, warum Ihr Chef es so gebaut hat. Möglicherweise weiß er / sie, dass es kein gutes Design ist, hat aber nicht das Wissen (oder eher die Zeit), es besser zu machen. Alle Änderungen, die Sie vorschlagen, werden wahrscheinlich positiver aufgenommen, wenn Sie die Gründe für das aktuelle Design mit Respekt angeben.
Pedro
5
He [the boss] had given me one of his databases before and it completely went against what I was taught (and read about) in school for RDBMS<- Willkommen in der realen Welt!
Möoz
5
Ich erinnere mich an mein Lieblingszitat aus relationalen Datenbanken: "Normalisieren, bis es weh tut, Denormalisieren, bis es funktioniert"
Jake

Antworten:

70

Das Leistungsargument ist normalerweise das intuitivste. Sie möchten insbesondere darauf hinweisen, wie schwierig es sein wird, gute Indizes in eine falsch normalisierte Datenbank einzufügen (Anmerkung: Es gibt Randfälle, in denen eine Denormalisierung die Leistung tatsächlich verbessern kann , aber wenn Sie beide mit relationalen Datenbanken unerfahren sind, wird dies wahrscheinlich nicht einfach sein siehe diese Fälle).

Ein weiteres Argument ist die Speichergröße. Eine denormalisierte Tabelle mit vielen Redundanzen erfordert viel mehr Speicherplatz. Dies wirkt sich auch auf die Leistung aus: Je mehr Daten Sie haben, desto langsamer werden Ihre Abfragen.

Es gibt auch ein Argument, das etwas schwieriger zu verstehen ist, aber in der Tat wichtiger, weil Sie es nicht lösen können, indem Sie mehr Hardware darauf werfen. Das ist das Problem der Datenkonsistenz. Eine ordnungsgemäß normalisierte Datenbank stellt von sich aus sicher, dass ein Produkt mit einer bestimmten ID immer den gleichen Namen hat. In einer denormalisierten Datenbank sind solche Inkonsistenzen jedoch möglich, weshalb besondere Sorgfalt erforderlich ist, um Inkonsistenzen zu vermeiden, die Programmierzeit in Anspruch nehmen und dennoch Fehler verursachen, die Sie bei der Kundenzufriedenheit kosten.

Philipp
quelle
19
Ein wesentlicher Vorteil der Denormalisierung ist das Data Warehousing , insbesondere wenn Sie über eine große Datenmenge verfügen, die sich garantiert nicht ändert und die Sie auf Kosten des Speicherplatzes schneller und effizienter abfragen möchten. Gute Antwort, dies ist nur eine Information für SQL-Neulinge, die sich nicht sicher sind, warum etwas anderes als 3NF wünschenswert wäre.
11
Ich bin nicht sicher, warum das Konsistenzargument "schwerer zu verstehen" ist. Mir scheint es viel einfacher: Wenn sich ein Wert ändert, müssen alle Kopien dieses Werts aktualisiert werden. Das Aktualisieren einer einzelnen Kopie ist weitaus weniger fehleranfällig als das Aktualisieren von Hunderten oder Tausenden Kopien derselben Daten. Dies gilt auch für Beziehungen zwischen Daten. (Wenn ich die Beziehung auf zwei Arten gespeichert habe, muss ich beide Kopien der Beziehung aktualisieren.) Dies ist ein äußerst häufiges Problem in denormalisierten DBs. Es ist sehr schwierig, diese Beschädigung in der Praxis zu verhindern (eine Ausnahme ist die Verwendung von materialisierten Ansichtstypen).
jpmc26
4
Dieser letzte Absatz sollte fett hervorgehoben werden. :-) Ohne Normalisierung kann die Datenintegrität nicht garantiert werden. Die Kontrolle der Eingabe ausschließlich auf der Business Logic-Ebene ist ein Kinderspiel, da jede nicht normalisierte Datenbank irgendwann eine Datenanomalie aufweist.
DanK
2
@IsmaelMiguel Es ist üblich, dass solche Stammdaten niemals unwiderruflich aus der Datenbank gelöscht werden. Sie löschen es nur im Hintergrund, indem Sie ein Flag setzen, das besagt, dass es nicht mehr verfügbar ist. In diesem speziellen Fall ist es eine gute Idee, eine Fremdschlüsselbeziehung zwischen Produkten und Bestellungen zu haben. Dies bedeutet, dass die Datenbank einen Fehler ausgibt, wenn Sie versuchen, ein Produkt zu löschen, auf das von Bestellungen verwiesen wird.
Philipp
24

Ich werde mit meinem Chef eine Datenbank implementieren müssen ...

Dediziertes Datenbankmanagement mit Software möglicherweise wesentlich einfacher (sorry, konnte nicht widerstehen ).

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName

Wenn es in dieser Datenbank nur darum geht, "aufzuzeichnen", welches Produkt wo, wann und von wem verkauft wurde, können Sie die Definition der "OK-Datenbank" möglicherweise so weit strecken, dass sie es abdeckt. Wenn diese Daten für irgendetwas anderes verwendet werden, ist es wirklich ziemlich arm.

Aber ...

Reagieren die Anwendungen / Abfragen, die diese Daten verwenden, schlecht / langsam? Wenn nicht, gibt es kein echtes Problem zu lösen. Sicher, es sieht hässlich aus und fühlt sich auch so an, aber wenn es funktioniert, werden Sie keine "Punkte" bekommen , wenn Sie vorschlagen, dass es "besser sein könnte".

Wenn Sie bestimmte Symptome (z. B. Probleme) finden, die auf eine schlechte Datenmodellierung zurückzuführen sind, sollten Sie eine bessere Lösung entwickeln. Nehmen Sie eine Kopie einer dieser "Datenbanken", normalisieren Sie die Daten und prüfen Sie, ob Ihre Lösung besser funktioniert. Wenn es erheblich besser ist (und ich würde voll und ganz erwarten, dass alle Aktualisierungsvorgänge für diese Daten massiv verbessert werden), gehen Sie zu Ihrem Chef zurück und zeigen Sie ihm die Verbesserung.

Es ist durchaus möglich, seine "Einzeltabellenansicht" der Daten mit ... nun ... Ansichten wiederherzustellen.

Phill W.
quelle
11
Der Widerstand gegen die weltanschauung einzelner Tabellen kommt häufig von jenen, die mit SQL unerfahren sind und keine Verknüpfungen verstehen - insbesondere in Bezug auf fehlende Daten, dh äußere Verknüpfungen.
Robbie Dee
6
@RobbieDee Häufig kommt es von Personen, die denormalisierte Daten gesehen haben, die durch Inkonsistenz beschädigt werden. Ich bin eine solche Person. Ich würde diese Art von Struktur nur in der Situation betrachten, die Phill vorschlägt: Dies ist eine Art Protokollierungs- / Berichtstabelle, in der Daten niemals aktualisiert werden oder nur aktualisiert werden, indem sie bereinigt und vollständig aus anderen Quellen abgeleitet werden.
jpmc26
2
Selbst wenn die Anwendung mit einer Datenbank wie dieser eine akzeptable Leistung erbringt, ist sie dennoch nicht so flexibel wie eine ordnungsgemäß normalisierte Datenbank. Wenn sich der Geschäftsname oder der Firmenname ändert, muss er überall aktualisiert werden und nicht nur in einem Geschäft oder einer Firmentabelle. In einigen Fällen ist dies tatsächlich das, was Sie möchten (z. B. wenn die Daten hauptsächlich zu Archivierungszwecken erfasst werden), aber wir müssen mehr über die spezifische Anwendung wissen.
Zach Lipton
1
@Zach: Einverstanden, deshalb ist das Verkaufsprotokoll möglicherweise ein akzeptabler Fall dafür. Angenommen, Sie möchten, dass jeder Verkauf mit dem Geschäft verknüpft wird, das zum Zeitpunkt des Verkaufs benannt war, und nicht mit dem "aktuellen Namen des Geschäfts". Wenn Sie dann versuchen, diesen zu "normalisieren", entsteht eine erhebliche Komplexität (da die Tabelle die Namen des Geschäfts aufzeichnet) müsste eine Serie im Laufe der Zeit sein, nicht nur ein Wert pro storeid)
Steve Jessop
Eine Faustregel wäre vielleicht, dass Sie, wenn die einzige Komplexität, die durch eine vorgeschlagene Normalisierung entsteht, darin besteht, dass einige Abfragen jetzt Verknüpfungen benötigen, um alle zu meldenden Spalten aufzunehmen, nicht laufen sollten, um diese Änderung vorzunehmen: )
Steve Jessop
14

Warum verbessert ein gutes relationales Schema eine Datenbank?

Die Antwort lautet: Eine Datenbank wird nicht immer verbessert. Sie sollten sich darüber im Klaren sein, dass das, was Ihnen wahrscheinlich beigebracht wurde, Dritte Normalform heißt .

In einigen Situationen sind andere Formulare gültig. Dies ist der Schlüssel zur Beantwortung Ihrer Frage. Ihr Beispiel sieht aus wie Erste Normalform , wenn Sie sich damit besser fühlen als bisher.

3NF-Regeln stellen Beziehungen zwischen Daten her, die eine Datenbank "verbessern":

  1. Verhindern Sie, dass ungültige Daten in Ihr System gelangen (wenn eine Beziehung 1 zu 1 ist, wird trotz des darüber geschriebenen Codes ein Fehler verursacht). Wenn Ihre Daten in der Datenbank konsistent sind, ist es weniger wahrscheinlich, dass sie zu Inkonsistenzen außerhalb Ihrer Datenbank führen.

  2. Es bietet eine Möglichkeit, Code zu validieren (z. B. ist eine Viele-zu-Eins-Beziehung ein Signal, um die Eigenschaften / Verhaltensweisen eines Objekts einzuschränken). Beim Schreiben von Code zur Verwendung der Datenbank bemerken Programmierer manchmal die Datenstruktur als Indikator für die Funktionsweise ihres Codes. Oder sie können nützliches Feedback geben, wenn die Datenbank nicht mit ihrem Code übereinstimmt. (Das ist leider eher Wunschdenken.)

  3. Stellen Sie Regeln bereit, mit denen Sie Fehler beim Erstellen einer Datenbank erheblich reduzieren können, damit Sie sie nicht auf der Grundlage von beliebigen Anforderungen erstellen, die zu einem beliebigen Zeitpunkt während der Lebensdauer einer Datenbank auftreten können. Stattdessen werten Sie die Informationen systematisch aus, um bestimmte Ziele zu erreichen.

  4. Ordnungsgemäße Datenbankstrukturen führen zu einer Leistungsverbesserung, indem Daten so verbunden werden, dass der Datenspeicher minimiert, Speicheraufrufe zum Abrufen von Daten minimiert, In-Memory-Ressourcen maximiert und / oder die Sortierung / Manipulation von Daten für das jeweilige Dataset im Vergleich zu Ihrer Abfrage minimiert werden Ausführung dagegen. Die "richtige" Struktur hängt jedoch von der Datenmenge, der Art der Daten, der Art der Abfrage, den Systemressourcen usw. ab. Durch die Normalisierung kann sich die Leistung verschlechtern (dh, wenn Sie alle Daten als eine Tabelle laden - das Verknüpfen kann sich verlangsamen eine Anfrage). Transaktionsverarbeitung (OLTP) und Business Intelligence (Data Warehouse) sind sehr unterschiedlich.

In einem kleinen Unternehmen mit kleinen Datenmengen stellen Sie möglicherweise fest, dass nichts an der aktuellen Situation falsch ist. Wenn Sie jedoch wachsen, wird es schwierig sein, das Problem später zu beheben, da die Systeme, die es verwenden, mit zunehmender Größe der Tabelle wahrscheinlich langsamer werden.

Normalerweise möchten Sie schnelle Transaktionen hervorheben, wenn ein Unternehmen wächst. Wenn Sie jedoch jetzt Zeit mit diesem Projekt verbringen, anstatt mit anderen Dingen, die das Unternehmen möglicherweise dringend benötigt, werden Sie dieses Problem möglicherweise nie haben, da Ihr Unternehmen nie wirklich wächst. Das ist die "Herausforderung vor der Optimierung" - wo Sie jetzt Ihre kostbare Zeit verbringen können.

Viel Glück!

Jim
quelle
4
Unerwähnt, aber ich denke, ein wichtiger Punkt für Programmierer ist, dass für das Bearbeiten einer "Sache" nur eine einzelne Zeile bearbeitet werden muss, anstatt die gesamte Datenbank zu durchlaufen, um diese einzelne Sache zu finden und zu ersetzen.
Slebetman
@slebetman Sie sollten niemals eine codeseitige Schleife haben, um mehrere Zeilen in einer einzelnen Tabelle zu aktualisieren, unabhängig davon, ob sie normalisiert ist. Verwenden Sie eine WHEREKlausel. Natürlich kann dies immer noch schief gehen, aber es ist in einer normalisierten Situation weniger wahrscheinlich, da Sie nur eine Zeile über den Primärschlüssel abgleichen müssen.
jpmc26
@ jpmc26: Mit dem Durchlaufen der Datenbank meine ich das Erstellen einer Abfrage zum Aktualisieren aller betroffenen Zeilen. Manchmal genügt ein einziges WO. Aber ich habe unheilige Strukturen gesehen, bei denen Unterauswahlen in derselben Tabelle erforderlich sind, um alle betroffenen Zeilen abzurufen, ohne dass sich dies auf Zeilen auswirkt, die sich nicht ändern sollten. Ich habe sogar Strukturen gesehen, in denen eine einzelne Abfrage die Aufgabe nicht erledigen kann (die Entität, die geändert werden muss, befindet sich in verschiedenen Spalten je nach Zeile)
slebetman
Viele hervorragende Antworten auf diese Frage, und das war keine Ausnahme.
Mike Chamberlain
11

Es gibt mehrere Gründe, warum die Verwendung eines großen "Gott-Tisches" schlecht ist. Ich werde versuchen, die Probleme mit einer zusammengestellten Beispieldatenbank zu veranschaulichen. Nehmen wir an, Sie versuchen, Sportereignisse zu modellieren. Wir werden sagen, dass Sie Spiele und die Mannschaften modellieren möchten, die in diesen Spielen spielen. Ein Design mit mehreren Tabellen könnte so aussehen (dies ist absichtlich sehr simpel, also bleiben Sie nicht an Orten hängen, an denen mehr Normalisierung angewendet werden könnte):

Teams
Id | Name | HomeCity

Games
Id | StartsAt | HomeTeamId | AwayTeamId | Location

und eine einzelne Tabellendatenbank würde so aussehen

TeamsAndGames
Id | TeamName | TeamHomeCity | GameStartsAt | GameHomeTeamId | GameAwayTeamId | Location

Betrachten wir zunächst die Erstellung von Indizes für diese Tabellen. Wenn ich für ein Team einen Index für die Heimatstadt benötigte, konnte ich ihn ganz einfach zur TeamsTabelle oder zur TeamsAndGamesTabelle hinzufügen . Denken Sie daran, dass jedes Mal, wenn Sie einen Index erstellen, dieser irgendwo auf der Festplatte gespeichert und aktualisiert werden muss, wenn Zeilen zur Tabelle hinzugefügt werden. Im Falle der TeamsTabelle ist dies ziemlich einfach. Ich habe ein neues Team zusammengestellt, die Datenbank aktualisiert den Index. Aber was ist mit für TeamsAndGames? Nun, das gleiche gilt von derTeamsBeispiel. Ich füge ein Team hinzu, der Index wird aktualisiert. Es passiert aber auch, wenn ich ein Spiel hinzufüge! Auch wenn dieses Feld für ein Spiel null ist, muss der Index trotzdem aktualisiert und auf der Festplatte für dieses Spiel gespeichert werden. Für einen Index hört sich das nicht schlecht an. Wenn Sie jedoch viele Indizes für die mehreren Entitäten in dieser Tabelle benötigen, verschwenden Sie viel Speicherplatz beim Speichern der Indizes und viel Prozessorzeit beim Aktualisieren, wenn sie nicht zutreffen.

Zweitens Datenkonsistenz. Bei Verwendung von zwei separaten Tabellen kann ich mithilfe von Fremdschlüsseln zwischen den GamesTabellen Teamsdefinieren, welche Teams in einem Spiel spielen. Und vorausgesetzt, ich mache die Spalten HomeTeamIdund AwayTeamIdnicht nullbar, stellt die Datenbank sicher, dass in jedem Spiel, das ich eingebe, 2 Teams vorhanden sind und diese Teams in meiner Datenbank vorhanden sind. Aber was ist mit dem Single-Table-Szenario? Nun, da diese Tabelle mehrere Entitäten enthält, sollten diese Spalten nullwertfähig sein (Sie könnten sie auch nicht nullwertfähig machen und Mülldaten dort hineinschieben, aber das ist nur eine schreckliche Idee). Wenn diese Spalten nullwertfähig sind, kann die Datenbank nicht mehr garantieren, dass beim Einfügen eines Spiels zwei Teams vorhanden sind.

Aber was ist, wenn Sie sich trotzdem dafür entscheiden? Sie richten die Fremdschlüssel so ein, dass diese Felder auf eine andere Entität in derselben Tabelle verweisen. Jetzt stellt die Datenbank jedoch nur sicher, dass diese Entitäten in der Tabelle vorhanden sind und nicht der richtige Typ. Sie könnten sehr leicht GameHomeTeamIddie ID eines anderen Spiels einstellen und die Datenbank wird sich überhaupt nicht beschweren. Wenn Sie dies im Szenario mit mehreren Tabellen versuchen würden, würde die Datenbank einen Fit auslösen.

Sie könnten versuchen, diese Probleme zu mindern, indem Sie sagen: "Nun, wir werden nur sicherstellen, dass wir das niemals im Code tun." Wenn Sie sicher sind, dass Sie beim ersten Mal fehlerfreien Code schreiben und jede seltsame Kombination von Dingen berücksichtigen können, die ein Benutzer möglicherweise versucht, fahren Sie fort. Ich persönlich bin mir nicht sicher, ob ich in der Lage bin, eines dieser Dinge zu tun. Deshalb lasse ich mir von der Datenbank ein zusätzliches Sicherheitsnetz geben.

(Dies wird noch schlimmer, wenn Sie in Ihrem Entwurf alle relevanten Daten zwischen den Zeilen kopieren, anstatt Fremdschlüssel zu verwenden. Rechtschreib- und andere Dateninkonsistenzen lassen sich nur schwer beheben. Woran lässt sich erkennen, dass "Jon" ein Rechtschreibfehler von "John" ist? "oder wenn es absichtlich war (weil es sich um zwei getrennte Personen handelt)?)

Drittens muss fast jede Spalte nullwertfähig sein oder mit kopierten oder fehlerhaften Daten gefüllt werden. Ein Spiel braucht kein TeamNameoder TeamHomeCity. Entweder benötigt jedes Spiel einen Platzhalter oder es muss nullbar sein. Und wenn es nullbar ist, nimmt die Datenbank gerne ein Spiel ohne TeamName. Es wird auch ein Team ohne Namen brauchen, auch wenn Ihre Geschäftslogik besagt, dass dies niemals passieren sollte.

Es gibt eine Handvoll anderer Gründe, warum Sie separate Tabellen benötigen (einschließlich der Wahrung der Entwicklerfreundlichkeit). Es gibt sogar einige Gründe, warum eine größere Tabelle besser sein könnte (Denormalisierung verbessert manchmal die Leistung). Diese Szenarien sind selten und weit verbreitet (und in der Regel am besten zu handhaben, wenn Sie Leistungsmessdaten haben, um zu zeigen, dass dies wirklich das Problem ist, nicht ein fehlender Index oder etwas anderes).

Entwickeln Sie schließlich etwas, das einfach zu warten ist. Nur weil es "funktioniert", heißt das nicht, dass es in Ordnung ist. Der Versuch, Gottestafeln (wie Gottesklassen) zu unterhalten, ist ein Albtraum. Sie bereiten sich erst später auf Schmerzen vor.

Becuzz
quelle
1
"Teams: Id | Name | HomeCity". Stellen Sie einfach sicher, dass Ihr Datenschema nicht zu Unrecht behauptet, dass Super Bowl XXXIV von den LA Rams gewonnen wurde. Während SB XXXIV in einer Abfrage für alle Meisterschaften erscheinen sollte , die von der derzeit als LA Rams bekannten Mannschaft gewonnen wurden. Es gibt bessere und schlechtere "Gott-Tische", und Sie haben sicherlich einen schlechten Tisch präsentiert. Besser wäre "Spiel-ID | Name der Heimmannschaft | Stadt der Heimmannschaft | Name der Auswärtsmannschaft | Stadt der Auswärtsmannschaft | Spiel beginnt um | etc ...". Dies ist ein erster Versuch, Informationen wie "New Orleans Saints @ Chicago Bears 1p Eastern" zu modellieren.
Steve Jessop
6

Zitat des Tages: " Theorie und Praxis sollten gleich sein ... in der Theorie "

Denormalisierte Tabelle

Ihre einzigartige Hold-It-All-Tabelle, die redundante Daten enthält, hat den Vorteil, dass die Berichterstellung in ihren Zeilen sehr einfach zu codieren und schnell auszuführen ist, da Sie keine Joins ausführen müssen. Aber das zu einem hohen Preis:

  • Es enthält redundante Kopien von Beziehungen (z . B. IngCompanyIDund vrCompanyName). Für die Aktualisierung von Stammdaten müssen möglicherweise wesentlich mehr Zeilen aktualisiert werden als in einem normalisierten Schema.
  • Es mischt alles. Sie können keine einfache Zugriffskontrolle auf Datenbankebene sicherstellen, indem Sie z. B. sicherstellen, dass Benutzer A nur Unternehmensinformationen und Benutzer B nur Produktinformationen aktualisieren kann.
  • Sie können Konsistenzregeln nicht auf Datenbankebene sicherstellen (z. B. Primärschlüssel, um zu erzwingen, dass es nur einen Firmennamen für eine Firmen-ID gibt).
  • Sie profitieren nicht vollständig vom DB-Optimierer, der optimale Zugriffsstrategien für eine komplexe Abfrage ermitteln kann, indem er die Größe normalisierter Tabellen und die Statistik mehrerer Indizes ausnutzt. Dies könnte den begrenzten Vorteil der Vermeidung von Verknüpfungen schnell ausgleichen.

Normalisierte Tabelle

Die oben genannten Nachteile sind Vorteile für das normalisierte Schema. Natürlich könnten die Abfragen etwas komplexer zu schreiben sein.

Kurz gesagt, das normalisierte Schema drückt die Struktur und die Beziehungen zwischen Ihren Daten viel besser aus. Ich werde provokativ sein und sagen, es ist der gleiche Unterschied zwischen der Disziplin, die für die Verwendung eines Satzes bestellter Büroschubladen erforderlich ist, und der einfachen Verwendung eines Abfalleimers.

Christophe
quelle
5

Ich denke, Ihre Frage besteht aus mindestens zwei Teilen:

1. Warum sollten Entitäten unterschiedlicher Typen nicht in derselben Tabelle gespeichert werden?

Die wichtigsten Antworten sind hier die Lesbarkeit des Codes und die Geschwindigkeit. A SELECT name FROM companies WHERE id = ?ist viel lesbarer als A SELECT companyName FROM masterTable WHERE companyId = ?und es ist weniger wahrscheinlich, dass Sie aus Versehen Unsinn abfragen (z. B. SELECT companyName FROM masterTable WHERE employeeId = ?wäre dies nicht möglich, wenn Unternehmen und Mitarbeiter in verschiedenen Tabellen gespeichert sind). Was die Geschwindigkeit betrifft, werden Daten aus einer Datenbanktabelle entweder durch sequentielles Lesen der vollständigen Tabelle oder durch Lesen aus einem Index abgerufen. Beide sind schneller, wenn die Tabelle / der Index weniger Daten enthält, und das ist der Fall, wenn die Daten in verschiedenen Tabellen gespeichert sind (und Sie nur eine der Tabellen / Indizes lesen müssen).

2. Warum sollten Entitäten eines einzelnen Typs in Unterentitäten aufgeteilt werden, die in verschiedenen Tabellen gespeichert sind?

Hier liegt der Grund hauptsächlich darin, Dateninkonsistenzen zu vermeiden. Beim Single-Table-Ansatz können Sie für ein Auftragsverwaltungssystem den Kundennamen, die Kundenadresse und die Produkt-ID des vom Kunden bestellten Produkts als eine Einheit speichern. Wenn ein Kunde mehrere Produkte bestellt hat, enthält Ihre Datenbank mehrere Instanzen des Namens und der Adresse des Kunden. Im besten Fall haben Sie gerade doppelte Daten in Ihrer Datenbank, was die Geschwindigkeit möglicherweise etwas verlangsamt. Ein schlimmerer Fall ist jedoch, dass jemand (oder ein Code) einen Fehler gemacht hat, als die Daten eingegeben wurden, sodass ein Unternehmen unterschiedliche Adressen in Ihrer Datenbank hat. Das alleine ist schon schlimm genug. Wenn Sie jedoch die Adresse eines Unternehmens anhand seines Namens abfragen (z. B.SELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1) Sie würden nur willkürlich eine der beiden Adressen zurückbekommen und würden nicht einmal bemerken, dass es eine Inkonsistenz gab. Aber jedes Mal, wenn Sie die Abfrage ausführen, erhalten Sie möglicherweise eine andere Adresse, je nachdem, wie Ihre Abfrage vom DBMS intern aufgelöst wird. Dies führt wahrscheinlich dazu, dass Ihre Anwendung an einer anderen Stelle beschädigt wird, und die eigentliche Ursache für diesen Fehler ist sehr schwer zu finden.

Mit dem Multi-Table-Ansatz würden Sie erkennen, dass es eine funktionale Abhängigkeit vom Firmennamen zur Firmenadresse gibt (wenn eine Firma nur eine Adresse haben kann), würden Sie das Tupel (companyName, companyAddress) in einer Tabelle speichern (z. B. company) und das Tupel (productId, companyName) in einer anderen Tabelle (zB order). Eine UNIQUEEinschränkung für die companyTabelle könnte dann erzwingen, dass jede Firma nur eine einzige Adresse in Ihrer Datenbank hat, so dass keine Inkonsistenz für Firmenadressen jemals auftreten kann.

Hinweis: In der Praxis würden Sie aus Leistungsgründen wahrscheinlich eine eindeutige Unternehmens-ID für jedes Unternehmen generieren und diese als Fremdschlüssel verwenden, anstatt den Unternehmensnamen direkt zu verwenden. Der allgemeine Ansatz bleibt jedoch derselbe.

Träumer
quelle
3

TL; DR - Sie entwerfen die Datenbank basierend darauf, wie sie in der Schule unterrichtet wurden.

Ich hätte diese Frage vor 10 Jahren schreiben können. Ich habe einige Zeit gebraucht, um zu verstehen, warum meine Vorgänger ihre Datenbanken so gestaltet haben, wie sie es getan haben. Sie arbeiten mit jemandem zusammen, der entweder:

  1. Sammelte die meisten Kenntnisse im Datenbankdesign mit Excel als Datenbank oder
  2. Sie wenden die besten Praktiken an, als sie die Schule verlassen haben.

Ich vermute nicht, dass es die Nummer 1 ist, da Sie tatsächlich ID-Nummern in Ihrer Tabelle haben, also nehme ich die Nummer 2 an.

Nachdem ich die Schule verlassen hatte, arbeitete ich für einen Laden, in dem AS / 400 (auch bekannt als IBM i) verwendet wurde. Ich fand einige seltsame Dinge in der Art und Weise, wie sie ihre Datenbanken entworfen haben, und begann zu befürworten, dass wir Änderungen vornehmen, um zu verfolgen, wie mir das Entwerfen von Datenbanken beigebracht wurde. (Ich war damals dumm)

Ein geduldiger älterer Programmierer brauchte, um mir zu erklären, warum die Dinge so gemacht wurden. Sie hatten das Schema nicht geändert, weil es dazu geführt hätte, dass Programme, die älter als ich waren, nicht mehr funktionierten. Der Quellcode für ein Programm hatte buchstäblich das Erstellungsdatum des Jahres vor meiner Geburt. Auf dem System, an dem wir arbeiteten, mussten ihre Programme alle Logik und Operationen implementieren, die der Abfrageplaner Ihrer Datenbank für Sie handhabt. (Sie können dies sehen, indem Sie EXPLAIN für eine Ihrer Abfragen ausführen.)

Er war auf dem neuesten Stand der Techniken, die ich implementieren wollte, aber es war wichtiger, das System am Laufen zu halten, als Änderungen vorzunehmen, "weil dies gegen das verstieß, was mir beigebracht wurde". Jedes neue Projekt, mit dem einer von uns begonnen hat, hat das Beziehungsmodell, das wir konnten, bestmöglich genutzt. Leider haben andere Programmierer / Berater aus dieser Zeit ihre Datenbanken immer noch so gestaltet, als würden sie mit den früheren Einschränkungen dieses Systems arbeiten.


Einige Beispiele dessen, was mir begegnet ist und nicht zum relationalen Modell passt:

  • Daten wurden als julianische Tagesnummern gespeichert, für die eine Verknüpfung mit einer Datumstabelle erforderlich war, um das tatsächliche Datum zu ermitteln.
  • Denormalisierte Tabellen mit sequentiellen Spalten desselben Typs (z. B. code1,code2, ..., code20)
  • CHAR-Spalten der Länge NxM, die ein Array von N Zeichenfolgen der Länge M darstellen.

Die Gründe, die mir für diese Entwurfsentscheidungen gegeben wurden, basierten alle auf den Einschränkungen des Systems, als die Datenbank zum ersten Mal entworfen wurde.

Daten - Mir wurde mitgeteilt, dass die Verwendung von Datumsfunktionen (welcher Monat oder Tag oder Wochentag) mehr Verarbeitungszeit in Anspruch nahm als die Erstellung einer Tabelle für jedes mögliche Datum mit all diesen Informationen.

Sequentielle Spalten desselben Typs - In der Programmierumgebung, in der sie sich befanden, konnte ein Programm eine Arrayvariable über einen Teil der Zeile erstellen. Und es war eine einfachere Möglichkeit, die Anzahl der Lesevorgänge zu verringern.

CHAR-Spalten mit einer Länge von NxM - Es war einfacher, Konfigurationswerte in eine Spalte zu verschieben, um Dateilesevorgänge zu reduzieren.

Ein schlecht durchdachtes Beispiel in C, das die Programmierumgebung widerspiegelt, die sie hatten:

#define COURSE_LENGTH 4
#define NUM_COURSES 4
#define PERIOD_LENGTH 2

struct mytable {
    int id;
    char periodNames[NUM_COURSES * PERIOD_LENGTH];  // NxM CHAR Column
    char course1[COURSE_LENGTH];
    char course2[COURSE_LENGTH];
    char course3[COURSE_LENGTH];
    char course4[COURSE_LENGTH];
};

...

// Example row
struct mytable row = {.id= 1, .periodNames="HRP1P2P8", .course1="MATH", .course2="ENGL", .course3 = "SCI ", .course4 = "READ"};

char *courses; // Pointer used to access the sequential columns
courses = (char *)&row.course1;


for(int i = 0; i < NUM_COURSES; i++) {

    printf("%d: %.*s -> %.*s\n",i+1, PERIOD_LENGTH, &row.periodNames[PERIOD_LENGTH * i], COURSE_LENGTH,&courses[COURSE_LENGTH*i]);
}

Ausgänge

1: HR -> MATH
2: P1 -> ENGL
3: P2 -> SCI
4: P8 -> READ

Nach dem, was mir gesagt wurde, galt ein Teil davon damals als best practice.

Core.B
quelle