@Bill Karwin descrive tre modelli di ereditarietà nel suo libro SQL Antipatterns, quando propone soluzioni all'antipattern SQL Entity-Attribute-Value. Questa è una breve panoramica:
Ereditarietà tabella singola (aka Ereditarietà tabella per gerarchia):
L'utilizzo di una singola tabella come nella prima opzione è probabilmente il design più semplice. Come hai accennato, a molti attributi specifici del sottotipo dovrà essere assegnato un NULL
valore sulle righe in cui questi attributi non si applicano. Con questo modello, avresti una tabella delle politiche, che sarebbe simile a questa:
+------+---------------------+----------+----------------+------------------+
| 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 -----/
Mantenere il design semplice è un vantaggio, ma i problemi principali con questo approccio sono i seguenti:
-
Quando si tratta di aggiungere nuovi sottotipi, è necessario modificare la tabella per accogliere gli attributi che descrivono questi nuovi oggetti. Questo può diventare rapidamente problematico quando hai molti sottotipi o se prevedi di aggiungere sottotipi regolarmente.
-
Il database non sarà in grado di imporre quali attributi si applicano e quali no, poiché non ci sono metadati per definire quali attributi appartengono a quali sottotipi.
-
Inoltre, non puoi applicare
NOT NULL
sugli attributi di un sottotipo che dovrebbero essere obbligatori. Dovresti gestirlo nella tua applicazione, che in generale non è l'ideale.
Ereditarietà della tabella concreta:
Un altro approccio per affrontare l'ereditarietà consiste nel creare una nuova tabella per ogni sottotipo, ripetendo tutti gli attributi comuni in ogni tabella. Ad esempio:
--// 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 |
+------+---------------------+------------------+
Questo progetto risolverà sostanzialmente i problemi identificati per il metodo a tabella singola:
-
Gli attributi obbligatori ora possono essere applicati con
NOT NULL
. -
L'aggiunta di un nuovo sottotipo richiede l'aggiunta di una nuova tabella invece di aggiungere colonne a una esistente.
-
Inoltre, non c'è il rischio che venga impostato un attributo inappropriato per un particolare sottotipo, come il
vehicle_reg_no
campo per una politica di proprietà. -
Non è necessario il
type
attributo come nel metodo a tabella singola. Il tipo è ora definito dai metadati:il nome della tabella.
Tuttavia questo modello presenta anche alcuni svantaggi:
-
Gli attributi comuni sono mescolati con gli attributi specifici del sottotipo e non esiste un modo semplice per identificarli. Neanche il database lo saprà.
-
Quando si definiscono le tabelle, è necessario ripetere gli attributi comuni per ogni tabella di sottotipo. Sicuramente non è SECCO.
-
La ricerca di tutte le politiche indipendentemente dal sottotipo diventa difficile e richiederebbe un sacco di
UNION
s.
Ecco come dovresti interrogare tutte le politiche indipendentemente dal tipo:
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;
Nota come l'aggiunta di nuovi sottotipi richiederebbe la modifica della query precedente con un ulteriore UNION ALL
per ogni sottotipo. Questo può facilmente portare a bug nella tua applicazione se questa operazione viene dimenticata.
Ereditarietà tabella di classe (nota anche come Ereditarietà tabella per tipo):
Questa è la soluzione che @David menziona nell'altra risposta. Crei una singola tabella per la tua classe base, che include tutti gli attributi comuni. Quindi creeresti tabelle specifiche per ogni sottotipo, la cui chiave primaria funge anche da chiave esterna per la tabella di base. Esempio:
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)
);
Questa soluzione risolve i problemi identificati negli altri due design:
-
Gli attributi obbligatori possono essere applicati con
NOT NULL
. -
L'aggiunta di un nuovo sottotipo richiede l'aggiunta di una nuova tabella invece di aggiungere colonne a una esistente.
-
Nessun rischio che venga impostato un attributo inappropriato per un particolare sottotipo.
-
Non è necessario il
type
attributo. -
Ora gli attributi comuni non vengono più mischiati con gli attributi specifici del sottotipo.
-
Possiamo rimanere ASCIUTTI, finalmente. Non è necessario ripetere gli attributi comuni per ogni tabella di sottotipo durante la creazione delle tabelle.
-
Gestione di un
id
con incremento automatico per le politiche diventa più semplice, perché questo può essere gestito dalla tabella di base, invece che ogni tabella di sottotipo le genera in modo indipendente. -
La ricerca di tutte le politiche indipendentemente dal sottotipo ora diventa molto semplice:No
UNION
è necessario - solo unSELECT * FROM policies
.
Considero l'approccio della tabella delle classi il più adatto nella maggior parte delle situazioni.
I nomi di questi tre modelli provengono dal libro di Martin Fowler Patterns of Enterprise Application Architecture.