@Bill Karwin popisuje tři modely dědičnosti ve své knize SQL Antipatterns, když navrhuje řešení pro antivzor SQL Entity-Attribute-Value. Toto je stručný přehled:
Dědičnost jedné tabulky (neboli dědění tabulky podle hierarchie):
Použití jedné tabulky jako ve vaší první možnosti je pravděpodobně nejjednodušší návrh. Jak jste zmínil, mnoho atributů, které jsou specifické pro podtyp, bude muset mít NULL
hodnota na řádcích, kde tyto atributy neplatí. S tímto modelem byste měli jednu tabulku politik, která by vypadala asi takto:
+------+---------------------+----------+----------------+------------------+
| id | date_issued | type | vehicle_reg_no | property_address |
+------+---------------------+----------+----------------+------------------+
| 1 | 2010-08-20 12:00:00 | MOTOR | 01-A-04004 | NULL |
| 2 | 2010-08-20 13:00:00 | MOTOR | 02-B-01010 | NULL |
| 3 | 2010-08-20 14:00:00 | PROPERTY | NULL | Oxford Street |
| 4 | 2010-08-20 15:00:00 | MOTOR | 03-C-02020 | NULL |
+------+---------------------+----------+----------------+------------------+
\------ COMMON FIELDS -------/ \----- SUBTYPE SPECIFIC FIELDS -----/
Udržování jednoduchého designu je výhodou, ale hlavní problémy tohoto přístupu jsou následující:
-
Pokud jde o přidávání nových podtypů, museli byste upravit tabulku tak, aby vyhovovala atributům, které tyto nové objekty popisují. To se může rychle stát problematickým, pokud máte mnoho podtypů nebo pokud plánujete přidávat podtypy pravidelně.
-
Databáze nebude schopna vynutit, které atributy se použijí a které ne, protože neexistují žádná metadata, která by definovala, které atributy patří ke kterým podtypům.
-
Nemůžete také vynutit
NOT NULL
o atributech podtypu, které by měly být povinné. To byste museli řešit ve své aplikaci, což obecně není ideální.
Dědičnost konkrétní tabulky:
Dalším přístupem k řešení dědičnosti je vytvoření nové tabulky pro každý podtyp opakováním všech společných atributů v každé tabulce. Například:
--// Table: policies_motor
+------+---------------------+----------------+
| id | date_issued | vehicle_reg_no |
+------+---------------------+----------------+
| 1 | 2010-08-20 12:00:00 | 01-A-04004 |
| 2 | 2010-08-20 13:00:00 | 02-B-01010 |
| 3 | 2010-08-20 15:00:00 | 03-C-02020 |
+------+---------------------+----------------+
--// Table: policies_property
+------+---------------------+------------------+
| id | date_issued | property_address |
+------+---------------------+------------------+
| 1 | 2010-08-20 14:00:00 | Oxford Street |
+------+---------------------+------------------+
Tento návrh v podstatě vyřeší problémy identifikované pro metodu jedné tabulky:
-
Povinné atributy lze nyní vynutit pomocí
NOT NULL
. -
Přidání nového podtypu vyžaduje přidání nové tabulky namísto přidávání sloupců do existující.
-
Neexistuje také žádné riziko, že pro určitý podtyp bude nastaven nevhodný atribut, například
vehicle_reg_no
pole pro politiku nemovitostí. -
Není potřeba
type
atribut jako u metody jedné tabulky. Typ je nyní definován metadaty:názvem tabulky.
Tento model má však také několik nevýhod:
-
Společné atributy jsou smíchány s atributy specifickými pro podtyp a neexistuje snadný způsob, jak je identifikovat. Databáze to také nebude vědět.
-
Při definování tabulek byste museli opakovat společné atributy pro každou tabulku podtypu. To rozhodně není SUCHÉ.
-
Hledání všech zásad bez ohledu na podtyp je obtížné a vyžadovalo by to spoustu
UNION
s.
Takto byste se museli dotazovat na všechny zásady bez ohledu na typ:
SELECT date_issued, other_common_fields, 'MOTOR' AS type
FROM policies_motor
UNION ALL
SELECT date_issued, other_common_fields, 'PROPERTY' AS type
FROM policies_property;
Všimněte si, že přidání nových podtypů by vyžadovalo úpravu výše uvedeného dotazu pomocí dalšího UNION ALL
pro každý podtyp. To může snadno vést k chybám ve vaší aplikaci, pokud je tato operace zapomenuta.
Dědičnost tabulky tříd (neboli dědění tabulky podle typu):
Toto je řešení, které @David zmiňuje v druhé odpovědi. Vytvoříte jednu tabulku pro svou základní třídu, která obsahuje všechny běžné atributy. Pak byste pro každý podtyp vytvořili specifické tabulky, jejichž primární klíč zároveň slouží jako cizí klíč k základní tabulce. Příklad:
CREATE TABLE policies (
policy_id int,
date_issued datetime,
-- // other common attributes ...
);
CREATE TABLE policy_motor (
policy_id int,
vehicle_reg_no varchar(20),
-- // other attributes specific to motor insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
CREATE TABLE policy_property (
policy_id int,
property_address varchar(20),
-- // other attributes specific to property insurance ...
FOREIGN KEY (policy_id) REFERENCES policies (policy_id)
);
Toto řešení řeší problémy identifikované v dalších dvou návrzích:
-
Povinné atributy lze vynutit pomocí
NOT NULL
. -
Přidání nového podtypu vyžaduje přidání nové tabulky namísto přidávání sloupců do existující.
-
Žádné riziko, že pro určitý podtyp bude nastaven nevhodný atribut.
-
Není potřeba
type
atribut. -
Nyní se již běžné atributy nemíchají se specifickými atributy podtypu.
-
Konečně můžeme zůstat V SUCHU. Při vytváření tabulek není potřeba opakovat společné atributy pro každý podtyp tabulky.
-
Správa automatického zvyšování
id
protože zásady se stávají jednodušší, protože to může být řešeno základní tabulkou, místo aby je každá tabulka podtypů generovala nezávisle. -
Hledání všech politik bez ohledu na podtyp je nyní velmi snadné:Žádné
UNION
je potřeba - stačíSELECT * FROM policies
.
Ve většině situací považuji za nejvhodnější přístup podle tabulky tříd.
Názvy těchto tří modelů pocházejí z knihy Martina Fowlera Patterns of Enterprise Application Architecture.