Bad Practice in Datenbank Design

Wann immer du als Entwickler eine Aufgabe erhältst, die auf vorhandenem Code basiert, musst du dich vielen Herausforderungen stellen. Eine dieser Herausforderungen – meistens als die anspruchsvollste – besteht darin, das Datenmodell einer Anwendung zu verstehen.

Du wirst normalerweise mit verwirrenden Tabellen, Ansichten, Spalten, Werten, gespeicherten Prozeduren, Funktionen, Constraints und Triggern konfrontiert, die viel Zeit in Anspruch nehmen, um Sinn zu ergeben. Sobald dies der Fall ist, bemerkst du viele Möglichkeiten, die gespeicherten Informationen zu verbessern und zu nutzen.

Wenn Du ein erfahrener Entwickler bist, wirst du wahrscheinlich auch Dinge bemerken, die man am Anfang besser gemacht werden könnten, z. B. Konstruktionsfehler.

In diesem Artikel erfährst du mehr über die häufigsten Bad Practices beim Datenbankdesign, warum sie schlecht sind und wie sie vermeiden können.

Den Zweck der Daten ignorieren

Daten werden gespeichert, um später verwendet zu werden, und das Ziel ist immer, die auf die effizienteste Weise zu speichern und abzurufen. Um dies zu erreichen, muss der Datenbankentwickler im Voraus wissen, was die Daten darstellen, wie und mit welcher Geschwindigkeit sie gewonnen werden, wie hoch das betriebliche Volumen sein wird (d.h. wie viel Daten erwartet werden) und schliesslich, wie es benutzt wird.

Beispielsweise hat ein industrielles Informationssystem, in dem täglich Daten manuell erfasst werden, nicht dasselbe Datenmodell wie ein industrielles System, in dem Informationen in Echtzeit generiert werden. Warum? Weil es sehr unterschiedlich ist, einige hundert oder tausende Datensätze pro Monat zu behandeln, verglichen mit dem Verwalten von Millionen von Datensätzen im selben Zeitraum. Die Entwickler müssen besondere Überlegungen anstellen, um die Effizienz und Verwendbarkeit der Datenbank aufrechtzuerhalten, wenn das Datenvolumen gross sein wird.

Allerdings ist natürlich nicht nur das Datenvolumen zu berücksichtigen, da der Zweck der Daten auch den Normalisierungsgrad, die Datenstruktur, die Datensatzgrösse und die allgemeine Implementierung des Gesamtsystems beeinflusst.

Den Zweck des zu erstellenden Datensystems genau zu kennen, führt zu Überlegungen bei der Auswahl der Datenbank-Engine, der zu entwerfenden Entitäten, der Datensatzgrösse und des Datensatzformats sowie der Richtlinien für die Datenbank-Engine-Verwaltung.

Das Ignorieren dieser Ziele führt zu Konstruktionen, die in ihren Grundlagen fehlerhaft sind, obwohl sie strukturell und mathematisch korrekt sind.

Schlechte Normalisierung

Das Entwerfen einer Datenbank ist keine deterministische Aufgabe. Zwei Datenbankentwickler können alle Regeln und Normalisierungsprinzipien für ein gegebenes Problem befolgen, und in den meisten Fällen erzeugen sie unterschiedliche Datenlayouts. Dies ist inhärent dem kreativen Charakter des Software Engineerings. Es gibt jedoch einige Analysetechniken, die in jedem Fall einen Sinn ergeben. Sie zu befolgen ist der beste Weg zu einer Datenbank die ihre beste Leistung erbringt.

Trotzdem sind wir häufig mit Datenbanken konfrontiert, die spontan entworfen wurden, ohne die grundlegendsten Regeln der Normalisierung zu befolgen. Wir müssen uns darüber klar sein: Jede Datenbank sollte zumindest auf die dritte Normalform normalisiert werden, da das Layout Ihre Entitäten am besten darstellt und deren Leistung am besten aus Abfragen und Einfügen, Aktualisieren und Löschen von Datensätzen besteht .

Wenn du mit Tabellen stolpern, die nicht mit 3NF, 2NF oder sogar 1NF übereinstimmen, solltest du diese Tabellen neu entwerfen. Der Aufwand, den du dafür investierst, macht sich sehr kurzfristig bezahlt.

Redundanz

Sehr verwandt mit dem vorherigen Punkt, da Redundanz zu reduzieren eines der Ziele der Normalisierung ist, ist Redundanz eine weitere Bad Practice, die oft vorkommt.

Redundante Felder und Tabellen sind ein Albtraum für Entwickler, da sie Business Logik erfordern, um mehrere Versionen derselben Informationen auf dem aktuellsten Stand zu halten. Dies ist ein Aufwand, der vermieden werden kann, wenn die Normalisierungsregeln sorgfältig befolgt werden. Manchmal kann eine Redundanz notwendig erscheinen, sie soll jedoch nur in sehr speziellen Fällen verwendet und eindeutig dokumentiert werden, um in zukünftigen Entwicklungen berücksichtigt zu werden.

Typische negative Auswirkungen der Redundanz sind eine unnötige Vergrösserung der Datenbank, Daten neigen zu Inkonsistenzen und verringern die Effizienz der Datenbank. Vor allem kann dies jedoch zu einer Beschädigung der Daten führen.

Schlechte referentielle Integrität (Constraints)

Referentielle Integrität ist eines der wertvollsten Werkzeuge, die Datenbank-Engines bieten um die Datenqualität auf höchstem Niveau zu halten. Wenn ab der Entwurfsphase keine oder nur sehr wenige Einschränkungen implementiert werden, muss die Datenintegrität vollständig auf die Business Logik angewiesen sein, sodass sie für menschliches Versagen anfällig ist.

Nicht nutzen von DB Engine-Funktionen

Wenn du eine Datenbank-Engine (DBE) verwendest, verfügst du über eine leistungsstarke Software für deine Datenhandhabungsaufgaben, die die Softwareentwicklung vereinfacht und gewährleistet, dass Informationen stets korrekt, sicher und verwendbar sind. Eine DBE bietet Dienste wie:

  •  Ansichten, die eine schnelle und effiziente Möglichkeit bieten, Daten einzusehen. Normalerweise für Abfragezwecke denormalisiert, ohne dass die Richtigkeit der Daten verloren geht.
  • Indizes, die Abfragen für Tabellen beschleunigen
  • Aggregatfunktionen zur Analyse von Informationen ohne Programmierung
  • Transaktionen oder Blöcke datenverändernder Sätze, die alle ausgeführt und festgeschrieben oder abgebrochen (rückgängig gemacht) werden, wenn etwas Unerwartetes auftritt, wodurch die Informationen immer in gültiger Weise erhalten bleiben
  • Locks, die Daten während der Ausführung von Transaktionen sicher und korrekt halten
  • Gespeicherte Prozeduren, die Programmierfunktionen bereitstellen, um komplexe Datenverwaltungsaufgaben zu ermöglichen
  • Funktionen, die anspruchsvolle Berechnungen und Datentransformationen ermöglichen
  • Constraints, die dabei helfen, die Richtigkeit der Daten zu gewährleisten und Fehler zu vermeiden
  • Trigger, mit denen Aktionen automatisiert werden, wenn Ereignisse in den Daten auftreten
  • Befehlsoptimierer (Ausführungsplaner), der unter der Haube ausgeführt wird, um sicherzustellen, dass jeder Satz optimal ausgeführt wird und die Ausführungspläne für zukünftige Anlässe aufbewahrt werden. Dies ist einer der besten Gründe für die Verwendung von Ansichten, gespeicherten Prozeduren und Funktionen, da ihre Ausführungspläne dauerhaft in der DBE aufbewahrt werden.

Wenn du diese Funktionen nicht kennst oder ignorierst, wird die Entwicklung auf einen äusserst unsicheren Weg und sicherlich zu Fehlern und zukünftigen Problemen führen.

Zusammengesetzte Primary Keys

Dies ist eine Art kontroverser Punkt, da viele Datenbankdesigner heutzutage davon sprechen, ein automatisch generiertes Feld mitInteger-ID als Primärschlüssel anstelle eines zusammengesetzten Schlüssels zu verwenden, der durch die Kombination von zwei oder mehr Feldern definiert wird. Dies wird derzeit als Best Practice definiert und ich stimme persönlich dem zu.

Dies ist jedoch nur eine Konvention und natürlich ermöglichen DBEs die Definition zusammengesetzter Primärschlüssel, die nach Ansicht einiger Designer unvermeidbar sind. Zusammen mit der Redundanz sind daher zusammengesetzte Primärschlüssel eine Designentscheidung.

Wenn du jedoch erwartest, dass deine Tabelle mit einem zusammengesetzten Primary Key Millionen Zeilen enthält, kann der Index, der den zusammengesetzten Schlüssel steuert, bis zu einem Punkt anwachsen, an dem die Leistung der CRUD-Operationen stark beeinträchtigt wird. In diesem Fall ist es viel besser, einen einfachen Primary Key mit Integer-ID zu verwenden, dessen Index kompakt genug ist, und die erforderlichen DBE-Einschränkungen festzulegen, um die Eindeutigkeit zu gewährleisten.

Schlechte Indizierung

Manchmal hast du eine Tabelle, die du nach vielen Spalten abfragen musst. Wenn die Tabelle wächst, wirst du feststellen, dass die SELECT-Anweisungen für diese Spalten langsamer werden. Wenn die Tabelle gross genug ist, denkst du logischerweise daran, für jede Spalte, die du für den Zugriff auf diese Tabelle verwenden, einen Index zu erstellen, und wirst fast sofort erkennt, dass sich die Leistung von SELECT verbessert, INSERTs, UPDATEs und DELETEs jedoch abnehmen. Dies ist natürlich darauf zurückzuführen, dass die Indizes mit der Tabelle synchronisiert werden müssen, was einen erheblichen Aufwand für die DBE bedeutet. Dies ist ein typischer Fall von Überindizierung, den du auf viele Arten lösen kannst. Wenn du beispielsweise nur einen Index für alle Spalten hast, die sich vom Primärschlüssel unterscheidet, den du zum Abfragen der Tabelle verwendest, kann das Anordnen dieser Spalten nach den am wenigsten verwendeten Spalten eine bessere Leistung bei allen CRUD-Vorgängen bieten als ein Index pro Spalte. Wenn du jedoch eine Tabelle ohne Index für Spalten hast, die für die Abfrage verwendet werden, führt dies, wie wir alle wissen, zu einer schlechten Leistung bei SELECTs.

Die Indexeffizienz hängt auch manchmal vom Spaltentyp ab. Indizes für INT-Spalten zeigen die bestmögliche Leistung, aber Indizes für VARCHAR, DATE oder DECIMAL (falls dies sinnvoll ist) sind nicht so effizient. Diese Überlegung kann sogar dazu führen, dass Tabellen neu gestaltet werden, auf die mit grösstmöglicher Effizienz zugegriffen werden muss.

Daher ist die Indizierung immer eine heikle Entscheidung, da zu viel Indizierung genauso schlecht wie zu wenig sein kann und der Datentyp der zu indizierenden Spalten einen grossen Einfluss auf das Endergebnis hat.

Schlechte Namenskonventionen

Dies ist etwas, mit dem Programmierer immer zu kämpfen haben, wenn sie mit einer vorhandenen Datenbank konfrontiert sind: Sie verstehen anhand der Namen von Tabellen und Spalten, welche Informationen in dieser Datenbank gespeichert sind. Oft gibt es keine andere Möglichkeit.

Der Tabellenname muss angeben, um welche Entität es sich handelt und jeder Spaltenname muss angeben, für welche Informationen er steht. Dies ist einfach, aber es wird kompliziert, wenn sich Tabellen auf einander beziehen müssen. Namen werden unordentlich und, was noch schlimmer ist, wenn Namenskonventionen mit unlogischen Normen verwirrend sind (z. B. „Spaltenname darf nicht länger als 8 Zeichen sein“). Die endgültige Folge ist, dass die Datenbank unlesbar wird.

Daher ist immer eine Namenskonvention erforderlich, wenn erwartet wird, dass die Datenbank mit der Applikation weiterentwickelt werden soll. Hier sind einige Richtlinien, um eine kurze, einfache und lesbare Datenbank festzulegen:

  • Keine Einschränkungen für die Länge von Tabellen- oder Spaltennamen. Es ist besser, einen beschreibenden Namen zu haben als ein Akronym, das sich niemand merken oder verstehen kann.
  • Gleiche Namen haben die gleiche Bedeutung. Vermeide Felder mit demselben Namen, aber unterschiedliche Typen oder Bedeutungen. Dies wird früher oder später verwirrend sein.
  • Seien Sie nicht redundant, wenn es nicht notwendig ist. In der Tabelle „Item“ müssen beispielsweise keine Spalten wie „ItemName“, „PriceOfItem“ oder ähnliche Namen vorhanden sein. „Name“ und „Preis“ sind genug.
  • Hüte dich vor DBE-reservierten Wörtern. Wenn eine Spalte als „Index“ bezeichnet werden soll, bei dem es sich um ein reserviertes SQL-Wort handelt, versuche es stattdessen als „IndexNumber“ zu bezeichnen.
  • Wenn du dich an die einfache Primary Key Regel hältst (Ganzzahl die automatisch generiert wird), nenne sie in jeder Tabelle „Id“.
  • Wenn du eine Verbindung zu einer anderen Tabelle herstellst, definiere den erforderlichen Foreign Key als Ganzzahl mit dem Namen „Id“ gefolgt vom Namen der verbundenen Tabelle (z. B. IdItem).
  • Für die Bennenung von Constraints, nutze eine Präfix, das den Constraint beschreibt (z. B. „PK“ oder „FK“), gefolgt vom Namen der betroffenen Tabelle(n). Wenn du Unterstriche ( _ ) verwendest, hilft das natürlich, die Lesbarkeit zu verbessern.
  • Verwende zum Benennen von Indizes das Präfix „IDX“, gefolgt vom Tabellennamen und der oder den Spalten des Index. Verwende auch „UNIQUE“ als Präfix oder Suffix, wenn der Index eindeutig ist und gegebenenfalls Unterstriche wo nötig.

Es gibt viele Richtlinien für die Benennung von Datenbanken im Internet, die diesen sehr wichtigen Aspekt des Datenbankdesigns näher beleuchten. Mit diesen grundlegenden Aspekten bekommst du jedoch zumindest auf eine lesbare Datenbank. Wichtig ist hier nicht die Grösse oder Komplexität Ihrer Namensrichtlinien, sondern die Konsequenz bei der Einhaltung dieser Richtlinien!

Einige abschliessende Bemerkungen

Datenbankdesign ist eine Kombination aus Wissen und Erfahrung. Die Softwareindustrie hat sich seit ihren Anfängen stark weiterentwickelt. Glücklicherweise gibt es genug Wissen, um Datenbankentwicklern dabei zu helfen, die besten Ergebnisse zu erzielen.

Es gibt gute Richtlinien für das Datenbankdesign sowie schlechte Praktiken und Dinge, die beim Datenbankdesign zu vermeiden sind. Triff einfach deine Wahl und bleib dabei.

Und vergiss nicht, dass nur durch experimentieren, Fehler und Erfolge, dass du lernst. Beginne also jetzt.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: