Die richtige Struktur für dieses Szenario ist ein SubClass / Inheritance-Modell und ist nahezu identisch mit dem Konzept, das ich in dieser Antwort vorgeschlagen habe: Heterogen geordnete Werteliste .
Das in dieser Frage vorgeschlagene Modell ist insofern ziemlich ähnlich, als die Animal
Entität den Typ (dh race
) und die Eigenschaften enthält, die für alle Typen gleich sind. Es sind jedoch zwei geringfügige Änderungen erforderlich:
Entfernen Sie die Felder Cat_ID und Dog_ID aus ihren jeweiligen Entitäten:
Der Schlüsselbegriff ist hier, dass alles ist Animal
, und zwar unabhängig von race
: Cat
, Dog
, Elephant
, und so weiter. Angesichts dieses Ausgangspunkts benötigt eine bestimmte race
von Animal
nicht wirklich eine separate Kennung, da:
- Das
Animal_ID
ist einzigartig
- die
Cat
, Dog
und alle zusätzlichen race
Einheiten in der Zukunft hinzugefügt dies nicht tun, indem sie sich, voll einen bestimmten darstellen Animal
; Sie haben nur dann eine Bedeutung, wenn sie in Kombination mit den in der übergeordneten Entität enthaltenen Informationen verwendet werden Animal
.
Daher ist die Animal_ID
Eigenschaft in der Cat
, Dog
etc Einheiten ist sowohl der PK und der FK an der Rückseite Animal
Einheit.
Unterscheiden Sie zwischen Arten von breed
:
Nur weil zwei Eigenschaften denselben Namen haben, bedeutet dies nicht unbedingt, dass diese Eigenschaften gleich sind, auch wenn der gleiche Name eine solche Beziehung impliziert . In diesem Fall ist das, was Sie wirklich haben, tatsächlich CatBreed
und DogBreed
als separate "Typen".
Erste Hinweise
- Das SQL ist spezifisch für Microsoft SQL Server (dh T-SQL). Seien Sie also vorsichtig mit Datentypen, da diese nicht in allen RDBMS gleich sind. Zum Beispiel verwende ich,
VARCHAR
aber wenn Sie etwas außerhalb des Standard-ASCII-Satzes speichern müssen, sollten Sie es wirklich verwenden NVARCHAR
.
- Die ID - Felder der „Typ“ Tabellen (
Race
, CatBreed
, und DogBreed
) sind nicht automatisch inkrementierende (dh IDENTITY in Bezug auf die T-SQL) , weil sie Anwendung Konstanten sind (dh sie sind Teil der Anwendung sind) , die statische Nachschlagewerte in der sind Datenbank und werden als enum
s in C # (oder anderen Sprachen) dargestellt. Wenn Werte hinzugefügt werden, werden sie in kontrollierten Situationen hinzugefügt. Ich behalte mir die Verwendung von Auto-Inkrement-Feldern für Benutzerdaten vor, die über die Anwendung eingehen.
- Die von mir verwendete Namenskonvention besteht darin, jede Unterklassentabelle beginnend mit dem Hauptklassennamen gefolgt vom Unterklassennamen zu benennen. Dies hilft beim Organisieren der Tabellen und zeigt deutlich (ohne die FKs zu betrachten) die Beziehung der Unterklassentabelle zur Hauptentitätstabelle an.
- Weitere Informationen zu Ansichten finden Sie im Abschnitt "Endgültige Bearbeitung" am Ende.
"Rasse" als "Rasse" -Spezifischer Ansatz
Diese erste Gruppe von Tabellen sind die Nachschlagetabellen / Typen:
CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE CatBreed
(
CatBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
CatBreedAttribute1 INT,
CatBreedAttribute2 VARCHAR(10)
-- other "CatBreed"-specific properties as needed
);
CREATE TABLE DogBreed
(
DogBreedID INT NOT NULL PRIMARY KEY,
BreedName VARCHAR(50),
DogBreedAttribute1 TINYINT
-- other "DogBreed"-specific properties as needed
);
Diese zweite Auflistung ist die Hauptentität "Tier":
CREATE TABLE Animal
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
Name VARCHAR(50)
-- other "Animal" properties that are shared across "Race" types
);
ALTER TABLE Animal
ADD CONSTRAINT [FK_Animal_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
Dieser dritte Satz von Tabellen sind die komplementären Unterklassenentitäten, die die Definition Race
von Animal
:
CREATE TABLE AnimalCat
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
CatBreedID INT NOT NULL, -- FK to CatBreed
HairColor VARCHAR(50) NOT NULL
-- other "Cat"-specific properties as needed
);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_CatBreed]
FOREIGN KEY (CatBreedID)
REFERENCES CatBreed (CatBreedID);
ALTER TABLE AnimalCat
ADD CONSTRAINT [FK_AnimalCat_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
CREATE TABLE AnimalDog
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
DogBreedID INT NOT NULL, -- FK to DogBreed
HairColor VARCHAR(50) NOT NULL
-- other "Dog"-specific properties as needed
);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_DogBreed]
FOREIGN KEY (DogBreedID)
REFERENCES DogBreed (DogBreedID);
ALTER TABLE AnimalDog
ADD CONSTRAINT [FK_AnimalDog_Animal]
FOREIGN KEY (AnimalID)
REFERENCES Animal (AnimalID);
Das Modell, das einen gemeinsam genutzten breed
Typ verwendet, wird nach dem Abschnitt "Zusätzliche Hinweise" angezeigt.
Zusätzliche Bemerkungen
- Das Konzept von
breed
scheint ein Brennpunkt für Verwirrung zu sein. Es wurde von jcolebrand (in einem Kommentar zu der Frage) vorgeschlagen, dass breed
es sich um eine Eigenschaft handelt, die von den verschiedenen race
s gemeinsam genutzt wird, und die beiden anderen Antworten haben sie als solche in ihre Modelle integriert. Dies ist jedoch ein Fehler, da die Werte für breed
nicht auf die verschiedenen Werte von aufgeteilt werden race
. Ja, mir ist bekannt, dass die beiden anderen vorgeschlagenen Modelle versuchen, dieses Problem zu lösen, indem sie race
ein Elternteil von machen breed
. Während dies das Beziehungsproblem technisch löst, hilft es nicht, die allgemeine Modellierungsfrage zu lösen, was mit nicht allgemeinen Eigenschaften zu tun ist oder wie mit einer zu umgehen ist race
, die keine hat breed
. Aber für den Fall, dass eine solche Eigenschaft garantiert über alle existieren würdeAnimal
s, ich werde auch eine Option dafür einschließen (unten).
- Die von vijayp und DavidN vorgeschlagenen Modelle (die identisch zu sein scheinen) funktionieren nicht, weil:
- Sie auch
- nicht zulassen, dass nicht allgemeine Eigenschaften gespeichert werden (zumindest nicht für einzelne Instanzen von
Animal
) oder
- erfordern, dass alle Eigenschaften für alle
race
s in der Animal
Entität gespeichert werden , was eine sehr flache (und nahezu nicht relationale) Art der Darstellung dieser Daten darstellt. Ja, die Leute tun dies die ganze Zeit, aber es bedeutet, dass viele NULL-Felder pro Zeile für die Eigenschaften vorhanden sind, die nicht für diesen bestimmten bestimmt sind, race
UND dass sie wissen, welche Felder pro Zeile mit dem jeweiligen race
Datensatz verknüpft sind .
- Sie erlauben nicht das Hinzufügen eines
race
von Animal
in der Zukunft, das nicht breed
als Eigenschaft hat. Und selbst wenn ALLE Animal
eine breed
haben, würde dies die Struktur nicht ändern, aufgrund dessen, was zuvor erwähnt wurde breed
: Das breed
hängt von der ab race
(dh breed
für Cat
ist nicht dasselbe wie breed
für Dog
).
"Rasse" als Common- / Shared-Property-Ansatz
Bitte beachten Sie:
Die folgende SQL kann in derselben Datenbank wie das oben dargestellte Modell ausgeführt werden:
- Die
Race
Tabelle ist die gleiche
- Der
Breed
Tisch ist neu
- Die drei
Animal
Tabellen wurden mit a angehängt2
- Selbst
Breed
wenn es sich um eine jetzt übliche Eigenschaft handelt, scheint es nicht richtig zu sein Race
, dies nicht in der Haupt- / Mutterentität vermerkt zu haben (selbst wenn es technisch relational korrekt ist). Also beide RaceID
und BreedID
sind in vertreten Animal2
. Um eine Nichtübereinstimmung zwischen dem RaceID
vermerkten in Animal2
und einem BreedID
anderen zu verhindern RaceID
, habe ich auf beiden eine FK hinzugefügt RaceID, BreedID
, die auf eine EINZIGARTIGE EINSCHRÄNKUNG dieser Felder in der Breed
Tabelle verweist . Normalerweise verachte ich es, einen FK auf eine EINZIGARTIGE EINSCHRÄNKUNG zu verweisen, aber hier ist einer der wenigen triftigen Gründe dafür. Eine EINZIGARTIGE EINSCHRÄNKUNG ist logischerweise ein "alternativer Schlüssel", der sie für diese Verwendung gültig macht. Bitte beachten Sie auch, dass in der Breed
Tabelle nur noch eine PK aktiviert ist BreedID
.
- Der Grund dafür, dass nicht nur eine PK für die kombinierten Felder und keine EINZIGARTIGE EINSCHRÄNKUNG verwendet wird, besteht darin, dass dieselbe
BreedID
über verschiedene Werte von wiederholt werden kann RaceID
.
- Der Grund dafür, dass PK und UNIQUE CONSTRAINT nicht umgeschaltet werden, liegt darin, dass dies möglicherweise nicht die einzige Verwendung von
BreedID
ist. Daher sollte es weiterhin möglich sein, auf einen bestimmten Wert von zu verweisen, Breed
ohne über den RaceID
verfügbaren Wert zu verfügen .
- Das folgende Modell funktioniert zwar, weist jedoch zwei potenzielle Mängel hinsichtlich des Konzepts einer gemeinsam genutzten Tabelle auf
Breed
(und deshalb bevorzuge ich die Race
spezifischen Breed
Tabellen).
- Es gibt eine implizite Annahme, dass ALLE Werte von
Breed
dieselben Eigenschaften haben. Es gibt in diesem Modell keine einfache Möglichkeit, unterschiedliche Eigenschaften zwischen Dog
"Rassen" und Elephant
"Rassen" zu haben. Es gibt jedoch noch eine Möglichkeit, dies zu tun, die im Abschnitt "Endgültige Bearbeitung" aufgeführt ist.
- Es gibt keine Möglichkeit,
Breed
ein Rennen über mehrere Rennen hinweg zu teilen . Ich bin mir nicht sicher, ob dies wünschenswert ist (oder vielleicht nicht im Konzept der Tiere, aber möglicherweise in anderen Situationen, in denen diese Art von Modell verwendet wird), aber dies ist hier nicht möglich.
CREATE TABLE Race
(
RaceID INT NOT NULL PRIMARY KEY,
RaceName VARCHAR(50) NOT NULL
);
CREATE TABLE Breed
(
BreedID INT NOT NULL PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race
BreedName VARCHAR(50)
);
ALTER TABLE Breed
ADD CONSTRAINT [UQ_Breed]
UNIQUE (RaceID, BreedID);
ALTER TABLE Breed
ADD CONSTRAINT [FK_Breed_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
CREATE TABLE Animal2
(
AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
RaceID INT NOT NULL, -- FK to Race, FK to Breed
BreedID INT NOT NULL, -- FK to Breed
Name VARCHAR(50)
-- other properties common to all "Animal" types
);
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Race]
FOREIGN KEY (RaceID)
REFERENCES Race (RaceID);
-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
ADD CONSTRAINT [FK_Animal2_Breed]
FOREIGN KEY (RaceID, BreedID)
REFERENCES Breed (RaceID, BreedID);
CREATE TABLE AnimalCat2
(
AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalCat2
ADD CONSTRAINT [FK_AnimalCat2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
CREATE TABLE AnimalDog2
(
AnimalID INT NOT NULL PRIMARY KEY,
HairColor VARCHAR(50) NOT NULL
);
ALTER TABLE AnimalDog2
ADD CONSTRAINT [FK_AnimalDog2_Animal2]
FOREIGN KEY (AnimalID)
REFERENCES Animal2 (AnimalID);
Final Edit (hoffentlich ;-)
- In Bezug auf die Möglichkeit (und dann Schwierigkeiten) des Umgangs mit unterschiedlichen Eigenschaften zwischen verschiedenen Arten von
Breed
, es ist möglich , das gleiche Unterklasse / Vererbungskonzept , aber mit beschäftigen Breed
als Haupteinheit. In diesem Setup hätte die Breed
Tabelle die Eigenschaften, die allen Typen von Breed
(genau wie die Animal
Tabelle) gemeinsam sind, und RaceID
würde den Typ von darstellen Breed
(genau wie in der Animal
Tabelle). Dann würden Sie Unterklasse - Tabellen wie BreedCat
, BreedDog
und so weiter. Für kleinere Projekte dies könnte „over-engineering“ betrachtet werden, sondern als Option wird für Situationen genannt , die davon profitieren würden.
Bei beiden Ansätzen ist es manchmal hilfreich, Ansichten als Verknüpfungen zu den vollständigen Entitäten zu erstellen. Betrachten Sie zum Beispiel:
CREATE VIEW Cats AS
SELECT an.AnimalID,
an.RaceID,
an.Name,
-- other "Animal" properties that are shared across "Race" types
cat.CatBreedID,
cat.HairColor
-- other "Cat"-specific properties as needed
FROM Animal an
INNER JOIN AnimalCat cat
ON cat.AnimalID = an.AnimalID
-- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
- Obwohl dies nicht Teil der logischen Entitäten ist, ist es ziemlich üblich, Überwachungsfelder in den Tabellen zu haben, um zumindest einen Eindruck davon zu bekommen, wann die Datensätze eingefügt und aktualisiert werden. Also in praktischer Hinsicht:
CreatedDate
Der Animal
Tabelle wird ein Feld hinzugefügt . Dieses Feld wird in keiner der Unterklassentabellen (z. B. AnimalCat
) benötigt, da die Zeilen, die für beide Tabellen eingefügt werden, innerhalb einer Transaktion gleichzeitig ausgeführt werden sollen.
LastModifiedDate
Der Animal
Tabelle und allen Unterklassentabellen wird ein Feld hinzugefügt . Dieses Feld wird nur aktualisiert, wenn diese bestimmte Tabelle aktualisiert wird: Wenn eine Aktualisierung in, AnimalCat
aber nicht in Animal
einer bestimmten Tabelle erfolgt AnimalID
, wird nur das LastModifiedDate
Feld in AnimalCat
festgelegt.