it-swarm.com.de

Wie modellieren Sie Vererbung in einer Datenbank effektiv?

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, aber ich möchte auch verstehen, wie andere Plattformen dieses Problem angehen.)

119
Even Mien

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 einen eigenen Tisch. Die Basisklasse enthält alle Basisklassenelemente, und jede von ihr abgeleitete Klasse 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 folgenden Tabellen führen:

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 mehrere Spalten wahrscheinlich spärlich sind. Eine Unterscheidungsspalte wird hinzugefügt, die dem System mitteilt, um welchen Zeilentyp es sich handelt.

In Anbetracht der obigen Klassen erhalten Sie folgende Tabelle:

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

Für alle Zeilen mit dem Zeilentyp 0 (Person) ist das Startdatum immer null.

Tabelle pro Beton (TPC)

Jede Klasse verfügt über eine eigene vollständige Tabelle, ohne dass Verweise auf andere Tabellen erforderlich sind.

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
141
Brad Wilson

Richtiges Datenbankdesign ist nichts anderes als richtiges Objektdesign.

Wenn Sie vorhaben, die Datenbank für andere Zwecke als die bloße Serialisierung Ihrer Objekte (z. B. Berichte, Abfragen, Verwendung mehrerer Anwendungen, Business Intelligence usw.) zu verwenden, empfehle ich keine einfache Zuordnung von Objekten zu Tabellen.

Viele Leute denken an eine Zeile in einer Datenbanktabelle als Entität (ich habe viele Jahre damit verbracht, in diesen Begriffen zu denken), aber eine Zeile ist keine Entität. Es ist ein Vorschlag. Eine Datenbankbeziehung (d. H. Eine Tabelle) repräsentiert eine Tatsachenfeststellung über die Welt. Das Vorhandensein der Zeile zeigt an, dass das Faktum wahr ist (und umgekehrt, dass das Fehlen des Faktums 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. Eine Vielzahl von Typen (die durch Vererbung, Assoziation, Aggregation oder vollständig unverbunden sind) kann teilweise in einer einzigen Beziehung gespeichert werden.

Fragen Sie sich am besten, welche Fakten möchten Sie speichern, auf welche Fragen möchten Sie Antworten, welche Berichte möchten Sie erstellen.

Sobald das richtige DB-Design erstellt wurde, ist es einfach, Abfragen/Ansichten zu 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 für den 10. bis 12. April eine Reservierung für ein Zimmer im Seaview Inn vorgenommen hat. Ist das ein Attribut der Kundeneinheit? Ist es ein Attribut der Hoteleinheit? Handelt es sich um eine Reservierungseinheit mit Eigenschaften, die Kunden und Hotel umfassen? Es könnte irgendeines oder alle diese Dinge in einem objektorientierten System sein. In einer Datenbank ist es keines dieser Dinge. Es ist einfach eine bloße Tatsache.

Berücksichtigen Sie die folgenden zwei Abfragen, um den Unterschied festzustellen. (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 irgendeiner anderen "Entität" zu beschäftigen.

Daher wird durch den Versuch, Fakten über die Welt zu speichern, anstatt zu versuchen, Entitäten mit Attributen zu speichern, eine richtige relationale Datenbank erstellt. Und wenn es erst einmal richtig entworfen wurde, können nützliche Abfragen, die während der Entwurfsphase nicht in Betracht gezogen wurden, leicht erstellt werden, da sich alle Fakten, die zur Erfüllung dieser Abfragen erforderlich sind, an den richtigen Stellen befinden.

122

Kurze Antwort: Sie nicht.

Wenn Sie Ihre Objekte serialisieren müssen, verwenden Sie ein ORM oder besser so etwas wie Activerecord oder Prevaylence.

Wenn Sie Daten speichern müssen, speichern Sie sie in einer relationalen Weise (achten Sie darauf, was Sie speichern, und beachten Sie, was Jeffrey L. Whitledge gerade gesagt hat), ohne dass dies durch Ihren Objektentwurf beeinträchtigt wird.

8
Marcin

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 mehrmals gesehen, dass eindeutige IDs für jede untergeordnete Tabelle gespeichert werden, während die FK gleichzeitig für die übergeordnete Tabelle beibehalten wird. Ein FK ist gerade genug und es ist noch besser, die Kaskadenaktivierung beim Löschen für die FK-Beziehung zwischen den Kind- und Basistabellen zu haben.

  • Wenn Sie in TPT nur die Basistabellendatensätze anzeigen, können Sie nicht feststellen, für welche untergeordnete Klasse der Datensatz steht. Dies ist manchmal erforderlich, wenn Sie eine Liste aller Datensätze laden möchten (ohne select für jede untergeordnete Tabelle auszuführen). Eine Möglichkeit, dies zu handhaben, besteht darin, eine Spalte zu haben, die den Typ der untergeordneten Klasse darstellt (ähnlich dem rowType-Feld in der TPH), wodurch 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 obigen 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;
7
imang

Es gibt zwei Hauptvererbungstypen, die Sie in einer Datenbank einrichten können: Tabelle pro Entität und Tabelle pro Hierarchie.

In Tabelle pro Entität haben Sie eine Basisentitätstabelle mit gemeinsamen Eigenschaften aller untergeordneten Klassen. Sie haben dann pro untergeordneter Klasse eine weitere Tabelle mit jeweils nur Eigenschaften, die für diese Klasse gelten. Sie sind 1: 1 durch ihre PKs verbunden

alt text

In Tabelle pro Hierarchie haben alle Klassen eine Tabelle gemeinsam, und optionale Eigenschaften können auf Null gesetzt werden. Ihr ist auch ein Diskriminatorfeld, das eine Zahl ist, die den Typ angibt, den der Datensatz derzeit enthält

alt text SessionTypeID ist ein Diskriminator

Ziele pro Hierarchie können schneller abgefragt werden, da Sie keine Joins benötigen (nur den Diskriminatorwert), wohingegen Ziele pro Entität komplexe Joins ausführen müssen, um den Typ eines Objekts zu erkennen und alle seine Daten zurückzuholen.

Bearbeiten: Die Bilder, die ich hier zeige, sind Screenshots eines Projekts, an dem ich arbeite. Das Asset-Image ist nicht vollständig, daher seine Leere. Es sollte jedoch hauptsächlich zeigen, wie es eingerichtet ist, und nicht, was in Ihre Tabellen eingefügt werden soll. Es ist von dir abhängig ;). Die Sitzungstabelle enthält Sitzungsinformationen für die virtuelle Zusammenarbeit und kann abhängig von der Art der Zusammenarbeit verschiedene Arten von Sitzungen umfassen.

4
mattlant

Mit SQL ALchemy (Python ORM) können Sie zwei Arten der Vererbung durchführen.

Die einzige, die ich erlebt habe, ist die Verwendung einer Einzeltabelle mit einer Unterscheidungssäule. In einer Schaf-Datenbank (kein Scherz!) Wurden beispielsweise alle Schafe in einer Tabelle gespeichert, und Rams und Ewes wurden mithilfe einer Geschlechterspalte 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 Schafes), und so weiter.

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.

Wiederholung einer ähnlichen Thread-Antwort

bei der O-R-Zuordnung wird die Vererbung einer übergeordneten Tabelle zugeordnet, wobei 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 Object-Zeile erstellen und die ID in beiden Zeilen verwenden

BEARBEITEN: Wenn Sie auch das Verhalten modellieren möchten, benötigen Sie eine Typ-Tabelle, in der die Vererbungsbeziehungen zwischen Tabellen aufgelistet sind und die Assembly und den Klassennamen angegeben sind, mit denen das Verhalten der einzelnen Tabellen implementiert wurde

scheint übertrieben zu sein, aber das hängt alles davon ab, wofür du es verwenden willst!

1
Steven A. Lowe

Beachten Sie, dass einige Datenbank-Engines bereits Vererbungsmechanismen wie Postgres bereitstellen. Schauen Sie sich die Dokumentation .

Als Beispiel würden Sie das in der obigen Antwort beschriebene Personen-/Mitarbeitersystem wie folgt abfragen:

/* Hier wird der Vorname aller Personen oder Mitarbeiter angezeigt. */
 SELECT firstname FROM Person; 
 
/* Hier wird nur das Startdatum aller Mitarbeiter angezeigt. */
 SELECT startdate FROM Employee; 

Dabei müssen Sie nicht besonders schlau sein.

1
Pierre