Sie verwenden die Vererbung (in der Entity-Relationship-Modellierung auch als „Unterklasse“ oder „Kategorie“ bezeichnet). Im Allgemeinen gibt es 3 Möglichkeiten, es in der Datenbank darzustellen:
- "Alle Klassen in einer Tabelle": Haben Sie nur eine Tabelle, die die Eltern- und alle Kindklassen "abdeckt" (d. h. mit allen Eltern- und Kindspalten), mit einer CHECK-Einschränkung, um sicherzustellen, dass die richtige Teilmenge von Feldern nicht NULL ist (d. h. zwei verschiedene Kinder "mischen sich nicht").
- "Konkrete Klasse pro Tisch": Haben Sie eine andere Tabelle für jedes untergeordnete Element, aber keine übergeordnete Tabelle. Dies erfordert, dass die Beziehungen der Eltern (in Ihrem Fall Inventory <- Storage) in allen Kindern wiederholt werden.
- "Klasse pro Tisch": Eine übergeordnete Tabelle und eine separate Tabelle für jedes Kind zu haben, was Sie versuchen. Dies ist am saubersten, kann aber etwas Leistung kosten (hauptsächlich beim Ändern von Daten, nicht so sehr beim Abfragen, da Sie direkt vom untergeordneten Element aus beitreten und das übergeordnete Element überspringen können).
Normalerweise bevorzuge ich den 3. Ansatz, erzwinge aber beide die Präsenz und die Exklusivität eines Kindes auf Anwendungsebene. Beides auf Datenbankebene zu erzwingen ist etwas umständlich, kann aber durchgeführt werden, wenn das DBMS zurückgestellte Einschränkungen unterstützt. Zum Beispiel:
CHECK (
(
(VAN_ID IS NOT NULL AND VAN_ID = STORAGE_ID)
AND WAREHOUSE_ID IS NULL
)
OR (
VAN_ID IS NULL
AND (WAREHOUSE_ID IS NOT NULL AND WAREHOUSE_ID = STORAGE_ID)
)
)
Dadurch wird sowohl die Exklusivität (aufgrund des CHECK
) und das Vorhandensein (durch die Kombination von CHECK
und FK1
/FK2
) des Kindes.
Leider unterstützt MS SQL Server keine zurückgestellten Beschränkungen, aber Sie können möglicherweise die gesamte Operation hinter gespeicherten Prozeduren "verstecken" und Clients verbieten, die Tabellen direkt zu ändern.
Nur die Exklusivität kann ohne verzögerte Einschränkungen erzwungen werden:
Der STORAGE_TYPE
ist ein Typdiskriminator, normalerweise eine Ganzzahl, um Platz zu sparen (im obigen Beispiel sind 0 und 1 Ihrer Anwendung "bekannt" und werden entsprechend interpretiert).
Der VAN.STORAGE_TYPE
und WAREHOUSE.STORAGE_TYPE
können berechnete (auch bekannt als "berechnete") Spalten sein, um Speicherplatz zu sparen und die Notwendigkeit für CHECK
zu vermeiden s.
--- BEARBEITEN ---
Berechnete Spalten würden unter SQL Server wie folgt funktionieren:
CREATE TABLE STORAGE (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE tinyint NOT NULL,
UNIQUE (STORAGE_ID, STORAGE_TYPE)
);
CREATE TABLE VAN (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE AS CAST(0 as tinyint) PERSISTED,
FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);
CREATE TABLE WAREHOUSE (
STORAGE_ID int PRIMARY KEY,
STORAGE_TYPE AS CAST(1 as tinyint) PERSISTED,
FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);
-- We can make a new van.
INSERT INTO STORAGE VALUES (100, 0);
INSERT INTO VAN VALUES (100);
-- But we cannot make it a warehouse too.
INSERT INTO WAREHOUSE VALUES (100);
-- Msg 547, Level 16, State 0, Line 24
-- The INSERT statement conflicted with the FOREIGN KEY constraint "FK__WAREHOUSE__695C9DA1". The conflict occurred in database "master", table "dbo.STORAGE".
Leider benötigt SQL Server für eine berechnete Spalte die in einem Foreign verwendet wird Schlüssel, der PERSISTIERT werden soll. Andere Datenbanken haben diese Einschränkung möglicherweise nicht (z. B. die virtuellen Spalten von Oracle), wodurch Speicherplatz eingespart werden kann.