Du bruger arven (også kendt i enhedsrelationsmodellering som "underklasse" eller "kategori"). Generelt er der 3 måder at repræsentere det i databasen:
- "Alle klasser i én tabel": Har kun én tabel, der "dækker" overordnet og alle underordnede klasser (dvs. med alle overordnede og underordnede kolonner), med en CHECK-begrænsning for at sikre, at den rigtige delmængde af felter ikke er NULL (dvs. to forskellige underordnede "blandes ikke").
- "Konkret klasse pr. tabel": Har et forskelligt bord for hvert barn, men ingen forældrebord. Dette kræver, at forældres forhold (i dit tilfælde Inventory <- Storage) gentages hos alle børn.
- "Klasse pr. bord": At have et forældrebord og et separat bord for hvert barn, hvilket er det, du forsøger at gøre. Dette er reneste, men kan koste en vis ydeevne (for det meste ved ændring af data, ikke så meget ved forespørgsler, fordi du kan deltage direkte fra barnet og springe over forælderen).
Jeg foretrækker normalt den 3. tilgang, men håndhæver både tilstedeværelsen og eksklusiviteten af et barn på ansøgningsniveau. At håndhæve begge dele på databaseniveau er en smule besværligt, men kan gøres, hvis DBMS understøtter udskudte begrænsninger. For eksempel:
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)
)
)
Dette vil håndhæve både eksklusiviteten (på grund af CHECK
). ) og tilstedeværelsen (på grund af kombinationen af CHECK
og FK1
/FK2
) af barnet.
Desværre understøtter MS SQL Server ikke udskudte begrænsninger, men du kan muligvis "skjule" hele operationen bag lagrede procedurer og forbyde klienter at ændre tabellerne direkte.
Kun eksklusiviteten kan håndhæves uden udskudte begrænsninger:
STORAGE_TYPE
er en typediskriminator, normalt et heltal for at spare plads (i eksemplet ovenfor er 0 og 1 "kendt" for din applikation og fortolket i overensstemmelse hermed).
VAN.STORAGE_TYPE
og WAREHOUSE.STORAGE_TYPE
kan beregnes (også kaldet "beregnede") kolonner for at spare lagerplads og undgå behovet for CHECK
s.
--- REDIGER ---
Beregnede kolonner ville fungere under SQL Server som denne:
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".
Desværre kræver SQL Server en beregnet kolonne som bruges i en fremmed nøglen til at blive VEDHOLDT. Andre databaser har muligvis ikke denne begrænsning (f.eks. Oracles virtuelle kolonner), hvilket kan spare noget lagerplads.