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?
quelle
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!Antworten:
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.
quelle
Dediziertes Datenbankmanagement mit Software möglicherweise wesentlich einfacher (sorry, konnte nicht widerstehen ).
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.
quelle
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":
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.
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.)
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.
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!
quelle
WHERE
Klausel. 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.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):
und eine einzelne Tabellendatenbank würde so aussehen
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
Teams
Tabelle oder zurTeamsAndGames
Tabelle 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 derTeams
Tabelle ist dies ziemlich einfach. Ich habe ein neues Team zusammengestellt, die Datenbank aktualisiert den Index. Aber was ist mit fürTeamsAndGames
? Nun, das gleiche gilt von derTeams
Beispiel. 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
Games
TabellenTeams
definieren, welche Teams in einem Spiel spielen. Und vorausgesetzt, ich mache die SpaltenHomeTeamId
undAwayTeamId
nicht 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
GameHomeTeamId
die 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
TeamName
oderTeamHomeCity
. Entweder benötigt jedes Spiel einen Platzhalter oder es muss nullbar sein. Und wenn es nullbar ist, nimmt die Datenbank gerne ein Spiel ohneTeamName
. 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.
quelle
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:
IngCompanyID
undvrCompanyName
). Für die Aktualisierung von Stammdaten müssen möglicherweise wesentlich mehr Zeilen aktualisiert werden als in einem normalisierten Schema.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.
quelle
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 ASELECT 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 (zBorder
). EineUNIQUE
Einschränkung für diecompany
Tabelle 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.
quelle
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:
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:
code1,code2, ..., code20
)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:
Nach dem, was mir gesagt wurde, galt ein Teil davon damals als best practice.
quelle