Wie modellieren Sie die Vererbung in einer Datenbank effektiv?

131

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.)

Sogar Mien
quelle
14
Wenn Sie an "Best Practice" interessiert sind, sind die meisten Antworten einfach falsch. Best Practice schreibt vor, dass RDb und App unabhängig sind. Sie haben völlig unterschiedliche Designkriterien. Daher ist das "Modellieren der Vererbung" in einer Datenbank (oder das Modellieren des RDb für eine einzelne App oder App-Sprache) eine sehr schlechte Praxis, die nicht informiert ist und gegen grundlegende RDb-Entwurfsregeln verstößt und diese lähmt.
PerformanceDBA
Mögliches Duplikat von So etwas wie Vererbung im Datenbankdesign
Steve Chambers
6
@PerformanceDBA Was ist Ihr Vorschlag, um eine Vererbung im DB-Modell zu vermeiden? Nehmen wir an, wir haben 50 verschiedene Lehrertypen und möchten diesen bestimmten Lehrer mit der Klasse verbinden. Wie würden Sie das erreichen, ohne Vererbung zu haben?
Svlada
1
@svlada. Die Implementierung in einem RDb ist unkompliziert, daher ist "Vererbung" erforderlich. Stellen Sie eine Frage, fügen Sie die Tabellendefinitionen und ein Beispiel hinzu, und ich werde sie im Detail beantworten. Wenn Sie es in OO-Begriffen tun, wird es ein königliches Durcheinander sein.
PerformanceDBA

Antworten:

161

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:

class Person {
    public int ID;
    public string FirstName;
    public string LastName;
}

class Employee : Person {
    public DateTime StartDate;
}

Würde zu Tabellen führen wie:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK, FK)
datetime startdate

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:

table Person
------------
int id (PK)
int rowtype (0 = "Person", 1 = "Employee")
string firstname
string lastname
datetime startdate

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:

table Person
------------
int id (PK)
string firstname
string lastname

table Employee
--------------
int id (PK)
string firstname
string lastname
datetime startdate
Brad Wilson
quelle
23
"Welche Sie wählen, hängt von Ihren Bedürfnissen ab" - bitte erläutern Sie dies, da ich denke, dass die Gründe für die Auswahl den Kern der Frage bilden.
Alex
12
Siehe meinen Kommentar zur Frage. Die Verwendung lustiger neuer Namen für existierende Rdb-Fachbegriffe führt zu Verwirrung. "TPT" ist ein Supertyp-Subtyp. "TPH" ist nicht normalisiert, ein grober Fehler. "TPH" ist noch weniger normalisiert, ein weiterer grober Fehler.
PerformanceDBA
45
Nur ein DBA würde davon ausgehen, dass die Denormalisierung immer ein Fehler ist. :)
Brad Wilson
7
Ich werde zwar zugeben, dass die Denormalisierung in einigen Fällen zu Leistungssteigerungen führt, dies ist jedoch ausschließlich auf eine unvollständige (oder nicht vorhandene) Trennung zwischen der logischen und der physischen Struktur von Daten im DBMS zurückzuführen. Leider leidet die Mehrheit der kommerziellen DBMS unter diesem Problem. @PerformanceDBA ist korrekt. Die Unternormalisierung ist ein Beurteilungsfehler, der die Datenkonsistenz für die Geschwindigkeit beeinträchtigt. Leider ist es eine Entscheidung, die ein DBA oder Entwickler niemals treffen müsste, wenn das DBMS richtig entworfen worden wäre. Für die Aufzeichnung bin ich kein DBA.
Kenneth Cochran
6
@ Brad Wilson. Nur ein Entwickler würde denormalisieren, "für die Leistung" oder auf andere Weise. Oft ist es keine De-Normalisierung, die Wahrheit ist, dass es nicht normalisiert ist. Dass De-Normalisierung oder Nicht-Normalisierung ein Fehler ist, eine Tatsache ist, die von der Theorie gestützt wird und von Millionen erfahren wird, ist keine "Vermutung".
PerformanceDBA
133

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.

Jeffrey L Whitledge
quelle
12
+1 Schließlich eine Insel des echten Wissens in einem Meer der Unwissenheit (und der Weigerung, etwas außerhalb ihres Bereichs zu lernen). Einverstanden ist es keine Zauberei: Wenn der RDb unter Verwendung von RDb-Prinzipien entworfen wurde, ist es mühelos, eine "Klasse" "abzubilden" oder "zu projizieren". Das Erzwingen der RDb in klassenbasierte Anforderungen ist einfach falsch.
PerformanceDBA
2
Interessante Antwort. Wie würden Sie vorschlagen, das Beispiel Person-Mitarbeiter in der akzeptierten Antwort zu modellieren?
Sevenforce
2
@ Sevenforce-Das DB-Design hängt wirklich von den Anforderungen des Systems ab, die nicht angegeben sind. Es gibt nicht annähernd genug Informationen, um zu entscheiden. In vielen Fällen kann etwas Ähnliches wie das "Table-per-Type" -Design angebracht sein, wenn es nicht sklavisch befolgt wird. Zum Beispiel ist das Startdatum wahrscheinlich eine gute Eigenschaft für ein Mitarbeiterobjekt, aber in der Datenbank sollte es wirklich ein Feld in der Beschäftigungstabelle sein, da eine Person mit mehreren Startdaten mehrmals eingestellt werden kann. Dies spielt keine Rolle für die Objekte (die die neuesten verwenden würden), ist jedoch in der Datenbank wichtig.
Jeffrey L Whitledge
2
Sicher, meine Frage betraf hauptsächlich die Art und Weise, wie Vererbung modelliert werden kann. Entschuldigung, dass es nicht klar genug war. Vielen Dank. Wie Sie bereits erwähnt haben, sollte es höchstwahrscheinlich eine EmploymentTabelle geben, in der alle Beschäftigungen mit ihren Startdaten erfasst werden. Wenn Employeres also wichtig ist, das aktuelle Beschäftigungsbeginn von a zu kennen , könnte dies ein geeigneter Anwendungsfall für a sein View, der diese Eigenschaft durch Abfragen einschließt? (Anmerkung: scheint wegen des '-' direkt nach meinem Nick habe ich keine Benachrichtigung über Ihren Kommentar erhalten)
Sevenforce
5
Dies ist ein echtes Juwel einer Antwort. Es wird einige Zeit brauchen, um wirklich einzudringen und etwas Übung zu benötigen, um richtig zu werden, aber es hat bereits meinen Denkprozess zum relationalen Datenbankdesign beeinflusst.
MarioDS
9

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.

Marcin
quelle
3
+1 Der Versuch, die Vererbung in einer Datenbank zu modellieren, ist eine Verschwendung guter relationaler Ressourcen.
Daniel Spiewak
7

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:

public class Shape {
int id;
Color color;
Thickness thickness;
//other fields
}

public class Rectangle : Shape {
Point topLeft;
Point bottomRight;
}

public class Circle : Shape {
Point center;
int radius;
}

Das Datenbankdesign für die oben genannten Klassen kann folgendermaßen aussehen:

table Shape
-----------
int id; (PK)
int color;
int thichkness;
int rowType; (0 = Rectangle, 1 = Circle, 2 = ...)

table Rectangle
----------
int ShapeID; (FK on delete cascade)
int topLeftX;
int topLeftY;
int bottomRightX;
int bottomRightY;

table Circle
----------
int ShapeID; (FK on delete cascade)  
int centerX;
int center;
int radius;
imang
quelle
4

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

Alt-Text

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

Alt-Text 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.

mattlant
quelle
Ich würde auch Target per Concrete-Klasse als nicht wirklich gut modellierte Vererbung betrachten und habe sie daher nicht gezeigt.
Mattlant
Können Sie eine Referenz hinzufügen, aus der die Abbildung stammt?
Chrysss
Wo sind die Bilder, über die Sie am Ende Ihrer Antwort sprechen?
Musa Haidari
1

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.

Per Hornshøj-Schierbeck
quelle
2
Warum glauben die Leute, dass die Normalisierung einer Datenbank die Leistung beeinträchtigt? Denken die Leute auch, dass das DRY-Prinzip die Codeleistung beeinträchtigt? Woher kommt diese Fehlwahrnehmung?
Steven A. Lowe
1
Möglicherweise, weil die Denormalisierung die Leistung verbessern kann, wodurch die Normalisierung sie relativ gesehen verschlechtert. Ich kann nicht sagen, dass ich damit einverstanden bin, aber wahrscheinlich ist es so gekommen.
Matthew Scharley
2
Zu Beginn hat die Normalisierung möglicherweise nur geringe Auswirkungen auf die Leistung. Mit zunehmender Anzahl von Zeilen werden jedoch effiziente JOINs mit der Zeit die umfangreicheren Tabellen übertreffen. Natürlich hat die Normalisierung andere, größere Vorteile - Konsistenz und mangelnde Redundanz usw.
Rob
1

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

create table Object (
    Id int NOT NULL --primary key, auto-increment
    Name varchar(32)
)
create table SubObject (
    Id int NOT NULL  --primary key and also foreign key to Object
    Description varchar(32)
)

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!

Steven A. Lowe
quelle
In dieser Diskussion ging es schließlich darum, jeder Tabelle ein paar Spalten hinzuzufügen, und nicht darum, die Vererbung zu modellieren. Ich denke, der Titel dieser Diskussion sollte geändert werden, um die Art der Frage und Diskussion besser widerzuspiegeln.
Sogar Mien
1

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.

Matthew Schinckel
quelle
1

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:

  / * Hier wird der Vorname aller Personen oder Mitarbeiter angezeigt * /
  SELECT Vorname FROM Person; 

  / * Hier wird nur das Startdatum aller Mitarbeiter angezeigt * /
  SELECT Startdatum FROM Employee;

Nach Wahl Ihrer Datenbank müssen Sie nicht besonders schlau sein!

Pierre
quelle