Was sind die Best Practices für die Modellierung der Vererbung in Datenbanken?
Was sind die Kompromisse (z. B. Abfragbarkeit)?
(Ich interessiere mich am meisten für SQL Server und .NET, möchte aber auch verstehen, wie andere Plattformen dieses Problem beheben.)
.net
sql-server
oop
inheritance
database-design
Sogar Mien
quelle
quelle
Antworten:
Es gibt verschiedene Möglichkeiten, die Vererbung in einer Datenbank zu modellieren. Welche Sie wählen, hängt von Ihren Bedürfnissen ab. Hier sind einige Optionen:
Tabelle pro Typ (TPT)
Jede Klasse hat eine eigene Tabelle. Die Basisklasse enthält alle Basisklassenelemente, und jede Klasse, die von ihr abgeleitet ist, verfügt über eine eigene Tabelle mit einem Primärschlüssel, der auch ein Fremdschlüssel für die Basisklassentabelle ist. Die Klasse der abgeleiteten Tabelle enthält nur die verschiedenen Elemente.
Also zum Beispiel:
Würde zu Tabellen führen wie:
Tabelle pro Hierarchie (TPH)
Es gibt eine einzige Tabelle, die die gesamte Vererbungshierarchie darstellt, was bedeutet, dass einige der Spalten wahrscheinlich spärlich sind. Eine Diskriminatorspalte wird hinzugefügt, die dem System mitteilt, um welchen Zeilentyp es sich handelt.
In Anbetracht der obigen Klassen erhalten Sie diese Tabelle:
Für alle Zeilen vom Zeilentyp 0 (Person) ist das Startdatum immer null.
Table-Per-Concrete (TPC)
Jede Klasse hat ihre eigene vollständig geformte Tabelle ohne Verweise auf andere Tabellen.
In Anbetracht der obigen Klassen erhalten Sie folgende Tabellen:
quelle
Das richtige Datenbankdesign ist nichts anderes als das richtige Objektdesign.
Wenn Sie die Datenbank nur für die Serialisierung Ihrer Objekte verwenden möchten (z. B. Berichte, Abfragen, Verwendung mehrerer Anwendungen, Business Intelligence usw.), empfehle ich keine einfache Zuordnung von Objekten zu Tabellen.
Viele Leute denken an eine Zeile in einer Datenbanktabelle als eine Entität (ich habe viele Jahre damit verbracht, in diesen Begriffen zu denken), aber eine Zeile ist keine Entität. Es ist ein Satz. Eine Datenbankbeziehung (dh eine Tabelle) repräsentiert eine Tatsachenaussage über die Welt. Das Vorhandensein der Zeile zeigt an, dass die Tatsache wahr ist (und umgekehrt zeigt ihre Abwesenheit an, dass die Tatsache falsch ist).
Mit diesem Verständnis können Sie sehen, dass ein einzelner Typ in einem objektorientierten Programm in einem Dutzend verschiedener Beziehungen gespeichert werden kann. Und eine Vielzahl von Typen (durch Vererbung, Assoziation, Aggregation oder völlig unabhängig verbunden) können teilweise in einer einzigen Beziehung gespeichert werden.
Fragen Sie sich am besten, welche Fakten Sie speichern möchten, auf welche Fragen Sie Antworten wünschen und welche Berichte Sie erstellen möchten.
Sobald das richtige DB-Design erstellt wurde, können Sie ganz einfach Abfragen / Ansichten erstellen, mit denen Sie Ihre Objekte für diese Beziehungen serialisieren können.
Beispiel:
In einem Hotelbuchungssystem müssen Sie möglicherweise die Tatsache speichern, dass Jane Doe vom 10. bis 12. April ein Zimmer im Seaview Inn reserviert hat. Ist das ein Attribut der Kundenentität? Ist es ein Attribut der Hoteleinheit? Ist es eine Reservierungseinheit mit Eigenschaften, die Kunden und Hotel umfassen? Es könnten einige oder alle dieser Dinge in einem objektorientierten System sein. In einer Datenbank ist dies keines dieser Dinge. Es ist einfach eine bloße Tatsache.
Berücksichtigen Sie die folgenden beiden Abfragen, um den Unterschied zu erkennen. (1) Wie viele Hotelreservierungen hat Jane Doe für das nächste Jahr? (2) Wie viele Zimmer sind für den 10. April im Seaview Inn gebucht?
In einem objektorientierten System ist die Abfrage (1) ein Attribut der Kundenentität und die Abfrage (2) ein Attribut der Hotelentität. Dies sind die Objekte, die diese Eigenschaften in ihren APIs verfügbar machen würden. (Offensichtlich können die internen Mechanismen, mit denen diese Werte erhalten werden, Verweise auf andere Objekte beinhalten.)
In einem relationalen Datenbanksystem würden beide Abfragen die Reservierungsbeziehung untersuchen, um ihre Nummern zu erhalten, und konzeptionell besteht keine Notwendigkeit, sich mit einer anderen "Entität" zu befassen.
Durch den Versuch, Fakten über die Welt zu speichern, anstatt zu versuchen, Entitäten mit Attributen zu speichern, wird eine ordnungsgemäße relationale Datenbank erstellt. Und sobald es richtig entworfen wurde, können nützliche Abfragen, die während der Entwurfsphase ungeahnt waren, leicht erstellt werden, da sich alle Fakten, die zur Erfüllung dieser Abfragen erforderlich sind, an den richtigen Stellen befinden.
quelle
Employment
Tabelle geben, in der alle Beschäftigungen mit ihren Startdaten erfasst werden. WennEmployer
es also wichtig ist, das aktuelle Beschäftigungsbeginn von a zu kennen , könnte dies ein geeigneter Anwendungsfall für a seinView
, der diese Eigenschaft durch Abfragen einschließt? (Anmerkung: scheint wegen des '-' direkt nach meinem Nick habe ich keine Benachrichtigung über Ihren Kommentar erhalten)Kurze Antwort: Sie nicht.
Wenn Sie Ihre Objekte serialisieren müssen, verwenden Sie ein ORM oder noch besser etwas wie Activerecord oder Prevaylence.
Wenn Sie Daten speichern müssen, speichern Sie sie relational (achten Sie darauf, was Sie speichern, und achten Sie darauf, was Jeffrey L Whitledge gerade gesagt hat), ohne dass dies von Ihrem Objektdesign betroffen ist.
quelle
TPT-, TPH- und TPC-Muster sind die Wege, die Sie gehen, wie von Brad Wilson erwähnt. Aber ein paar Anmerkungen:
Untergeordnete Klassen, die von einer Basisklasse erben, können als schwache Entitäten für die Basisklassendefinition in der Datenbank angesehen werden. Dies bedeutet, dass sie von ihrer Basisklasse abhängig sind und ohne diese nicht existieren können. Ich habe schon oft gesehen, dass eindeutige IDs für jede untergeordnete Tabelle gespeichert werden, während die FK in der übergeordneten Tabelle bleibt. Ein FK ist gerade genug und es ist noch besser, die Kaskade beim Löschen für die FK-Beziehung zwischen der untergeordneten Tabelle und der Basistabelle zu aktivieren.
Wenn Sie in TPT nur die Basistabellendatensätze anzeigen, können Sie nicht finden, welche untergeordnete Klasse der Datensatz darstellt. Dies ist manchmal erforderlich, wenn Sie eine Liste aller Datensätze laden möchten (ohne dies
select
für jede untergeordnete Tabelle zu tun ). Eine Möglichkeit, dies zu handhaben, besteht darin, eine Spalte zu haben, die den Typ der untergeordneten Klasse darstellt (ähnlich dem Feld rowType im TPH), sodass TPT und TPH irgendwie gemischt werden.Angenommen, wir möchten eine Datenbank entwerfen, die das folgende Formklassendiagramm enthält:
Das Datenbankdesign für die oben genannten Klassen kann folgendermaßen aussehen:
quelle
Es gibt zwei Hauptvererbungstypen, die Sie in einer Datenbank einrichten können: Tabelle pro Entität und Tabelle pro Hierarchie.
In der Tabelle pro Entität befindet sich eine Basisentitätstabelle mit gemeinsamen Eigenschaften aller untergeordneten Klassen. Sie haben dann pro untergeordneter Klasse eine andere Tabelle mit jeweils nur Eigenschaften, die für diese Klasse gelten. Sie sind 1: 1 durch ihre PKs verbunden
In der Tabelle pro Hierarchie haben alle Klassen eine Tabelle gemeinsam genutzt, und optionale Eigenschaften können auf Null gesetzt werden. Es handelt sich auch um ein Diskriminatorfeld, bei dem es sich um eine Zahl handelt, die den Typ angibt, den der Datensatz derzeit enthält
SessionTypeID ist ein Diskriminator
Das Ziel pro Hierarchie ist schneller abzufragen, da Sie keine Verknüpfungen benötigen (nur den Diskriminatorwert), während das Ziel pro Entität komplexe Verknüpfungen ausführen muss, um zu erkennen, um welchen Typ es sich handelt, und um alle Daten erneut abzurufen.
Bearbeiten: Die Bilder, die ich hier zeige, sind Screenshots eines Projekts, an dem ich arbeite. Das Asset-Image ist nicht vollständig, daher die Leere, aber es sollte hauptsächlich zeigen, wie es eingerichtet ist und nicht, was in Ihre Tabellen eingefügt werden soll. Das ist dir überlassen ;). Die Sitzungstabelle enthält Sitzungsinformationen für die virtuelle Zusammenarbeit und kann je nach Art der Zusammenarbeit verschiedene Arten von Sitzungen umfassen.
quelle
Sie würden Ihre Datenbank normalisieren und das würde tatsächlich Ihre Vererbung widerspiegeln. Es kann zu Leistungseinbußen kommen, aber so ist es beim Normalisieren. Sie müssen wahrscheinlich gesunden Menschenverstand verwenden, um das Gleichgewicht zu finden.
quelle
Wiederholung einer ähnlichen Thread-Antwort
Bei der ODER-Zuordnung wird die Vererbung einer übergeordneten Tabelle zugeordnet, in der die übergeordnete und die untergeordnete Tabelle denselben Bezeichner verwenden
beispielsweise
SubObject hat eine Fremdschlüsselbeziehung zu Object. Wenn Sie eine SubObject-Zeile erstellen, müssen Sie zuerst eine Objektzeile erstellen und die ID in beiden Zeilen verwenden
BEARBEITEN: Wenn Sie auch das Verhalten modellieren möchten, benötigen Sie eine Typtabelle, in der die Vererbungsbeziehungen zwischen Tabellen aufgelistet und der Assembly- und Klassenname angegeben sind, der das Verhalten jeder Tabelle implementiert hat
scheint übertrieben, aber das hängt alles davon ab, wofür Sie es verwenden möchten!
quelle
Mit SQL ALchemy (Python ORM) können Sie zwei Arten der Vererbung durchführen.
Ich habe Erfahrung damit, eine Einzeltabelle zu verwenden und eine diskriminierende Spalte zu haben. Zum Beispiel speicherte eine Schaf-Datenbank (kein Scherz!) Alle Schafe in einer Tabelle, und Widder und Mutterschafe wurden unter Verwendung einer Geschlechtsspalte in dieser Tabelle behandelt.
Auf diese Weise können Sie alle Schafe abfragen und alle Schafe abrufen. Oder Sie können nur nach Ram abfragen, und es werden nur Rams abgerufen. Sie können auch Dinge tun, wie eine Beziehung haben, die nur ein Widder sein kann (dh der Vater eines Schafs), und so weiter.
quelle
Beachten Sie, dass einige Datenbankmodule bereits Vererbungsmechanismen wie Postgres bereitstellen . Schauen Sie sich die Dokumentation an .
In einem Beispiel würden Sie das in einer Antwort oben beschriebene Personen- / Mitarbeitersystem abfragen:
Nach Wahl Ihrer Datenbank müssen Sie nicht besonders schlau sein!
quelle